Почему `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).