Почему `s => x.Append(s)` может передаваться как Action <string>, но `x.Append` не может?

Я заметил что-то странное при попытке передать метод StringBuilder Append функции, которая приняла Action<string>.

public void DoStuff(Action<string> handler)
{
    // Do stuff, call handler("data");
}

В целях тестирования я просто хочу записать данные в StringBuilder, поэтому я попытался вызвать его следующим образом:

var output = new StringBuilder();
DoStuff(output.Append);

Однако это дает ошибку компиляции, потому что метод Append не соответствует требуемой сигнатуре (он возвращает ссылку обратно на StringBuilder, а не пустоту, как хочет мой метод):

'System.Text.StringBuilder System.Text.StringBuilder.Append(string)' имеет неправильный тип возврата

Не думая, я изменил код на это:

var output = new StringBuilder();
DoStuff(s => output.Append(s));

Этот скомпилированный файл.

Тогда я запутался; понимая, что s => output.Append(s) должен также возвращать StringBuilder, не являются ли они одинаковыми?

Итак, почему это работает? Зачем s => output.Append(s) вернуть возвращаемое значение молча, но output.Append не может?

Ответы

Ответ 1

s => output.Append(s) создает новое лямбда-выражение, которое выводится (из контекста), чтобы иметь возвращаемый тип void.
Следовательно, значение тела выражения игнорируется.
Это скомпилировано для отдельного метода, который вызывает Append() и возвращает void (это точно соответствует делегату)

В отличие от этого, когда вы пытаетесь преобразовать группу методов в делегат, преобразование должно точно соответствовать.

В спецификации (§ 6.5) говорится:

В частности, анонимная функция F совместима с типом делегирования D, если:

  • Если тело F является выражением, и либо D имеет тип возврата void, либо F является асинхронным, а D имеет задание типа возврата, тогда, когда каждому параметру F задан тип соответствующего параметра в D, тело F является допустимым выражением (см. § 7), которое было бы разрешено как выражение-выражение (§8.6).