В чем разница между новым Action () и лямбдой? - PullRequest
39 голосов
/ 19 апреля 2009

Итак, когда я пишу что-то вроде этого

Action action = new Action(()=>_myMessage = "hello");

Refactor Pro! Выделяет это как создание избыточного делегата и позволяет сократить его до

Action action = () => _myMessage="hello";

И это обычно прекрасно работает. Обычно , но не всегда. Например, Rhino Mocks имеет метод расширения с именем Do:

IMethodOptions<T> Do(Delegate action);

Здесь передача в первой версии работает, а во второй - нет. Что именно здесь происходит под одеялом?

Ответы [ 3 ]

58 голосов
/ 20 апреля 2009

Первая версия эффективно делает:

Action tmp = () => _myMessage = "hello";
var action = new Action(tmp);

Проблема, с которой вы сталкиваетесь, заключается в том, что компилятор должен знать, в какой тип делегата (или дерева выражений) должно быть преобразовано лямбда-выражение. Вот почему это:

var action = () => _myMessage="hello";

фактически не компилируется - это может быть любой тип делегата без параметров и либо без возвращаемого значения, либо с тем же типом возврата, что и _myMessage (предположительно string) Например, все они действительны:

Action action = () => _myMessage="hello";
Func<string> action = () => _myMessage="hello";
MethodInvoker action = () => _myMessage="hello";
Expression<Action> = () => _myMessage="hello";
// etc

Как мог компилятор C # определить, каким должен быть тип action, если он был объявлен с помощью var?

Самый простой способ обойти это при вызове метода (для вашего примера Rhino Mocks) - разыграть:

methodOptions.Do((Action) (() => _myMessage = "hello"));
9 голосов
/ 20 апреля 2009

Проверено ли, что вторая строка действительно компилируется? Он не должен компилироваться, потому что C # не поддерживает присвоение лямбда-выражения неявно типизированной переменной (CS0815). Эта строка будет работать в VB.Net, потому что она поддерживает создание анонимного делегата (начиная с VB 9.0).

Версия Rhino Mocks не компилируется по той же причине, по которой вторая строка не должна компилироваться. C # не будет автоматически выводить тип для лямбда-выражения. Лямбда-выражения должны использоваться в контексте, в котором можно определить тип делегата, для которого они предназначены. Первая строка прекрасно работает, потому что предполагаемый тип понятен: Действие. Версия Rhino Mocks не работает, потому что Delegate больше похож на абстрактный тип делегата. Это должен быть конкретный тип делегата, такой как Action или Func.

Для подробного обсуждения этой темы вы должны прочитать записи Эрика Липперта в блоге на эту тему: http://blogs.msdn.com/ericlippert/archive/2007/01/11/lambda-expressions-vs-anonymous-methods-part-two.aspx

1 голос
/ 11 мая 2018

Действие - это особый тип делегата. Таким образом, если вы используете лямбду, вы не поймете, какой тип вы хотите использовать, поскольку их много (Action, Func, ...) .

Чтобы преодолеть эту потребность в приведении (что в большинстве случаев медленно) , вы можете изменить параметр базовой функции с Делегата на Действие:

IMethodOptions<T> Do(Action action);

Таким образом, вы можете использовать оба утверждения и не будут иметь различий:

Action action = new Action(()=>_myMessage = "hello"); 
Action action = () => _myMessage="hello";

Если это невозможно, тогда я предлагаю использовать новое действие (() => {}) вместо приведения, это будет быстрее.

Пожалуйста, прочитайте следующую ссылку для получения дополнительной информации о действии и делегате: https://docs.microsoft.com/en-gb/dotnet/api/system.action?view=netframework-4.7.1#definition

...