В чем разница между лямбдами и делегатами в .NET Framework? - PullRequest
71 голосов
/ 16 сентября 2008

Мне часто задают этот вопрос, и я подумала, что хочу получить информацию о том, как лучше всего описать разницу.

Ответы [ 17 ]

83 голосов
/ 16 сентября 2008

На самом деле это две совершенно разные вещи. «Делегат» - это имя переменной, которая содержит ссылку на метод или лямбду, а лямбда - это метод без постоянного имени.

Лямбды очень похожи на другие методы, за исключением пары тонких различий.

  1. Обычный метод определен в «выражении» и привязан к постоянному имени, тогда как лямбда определяется «на лету» в «выражении» и не имеет постоянное имя.
  2. Некоторые лямбда-выражения можно использовать с деревьями выражений .NET, а методы - нет.

Делегат определяется следующим образом:

delegate Int32 BinaryIntOp(Int32 x, Int32 y);

Переменной типа BinaryIntOp может быть назначен либо метод, либо labmda, если сигнатура одна и та же: два аргумента Int32 и возврат Int32.

Лямбда может быть определена так:

BinaryIntOp sumOfSquares = (a, b) => a*a + b*b;

Следует также отметить, что хотя общие типы Func и Action часто рассматриваются как «лямбда-типы», они аналогичны любым другим делегатам. Приятно, что они по существу определяют имя для любого типа делегата, который вам может понадобиться (до 4 параметров, хотя вы, конечно, можете добавить и свой собственный). Поэтому, если вы используете широкий спектр типов делегатов, но не более одного раза, вы можете избежать загромождения своего кода объявлениями делегатов с помощью Func и Action.

Вот иллюстрация того, как Func и Action "не только для лямбд":

Int32 DiffOfSquares(Int32 x, Int32 y)
{
  return x*x - y*y;
}

Func<Int32, Int32, Int32> funcPtr = DiffOfSquares;

Еще одна полезная вещь, которую нужно знать, это то, что типы делегатов (не сами методы) с одинаковой подписью, но разными именами не будут неявно приводиться друг к другу. Это включает в себя делегатов Func и Action. Однако, если подпись идентична, вы можете явно привести между ними.

Пройдя лишнюю милю .... В C # функции гибкие, с использованием лямбд и делегатов. Но в C # нет «первоклассных функций». Вы можете использовать имя функции, назначенное переменной-делегату, чтобы по существу создать объект, представляющий эту функцию. Но это действительно трюк с компилятором. Если вы начнете оператор, написав имя функции, за которым следует точка (т. Е. Попытайтесь получить доступ к элементам самой функции), вы обнаружите, что там нет членов, на которые можно ссылаться. Даже те, что из Object. Это не позволяет программисту делать полезные (и потенциально опасные) вещи, такие как добавление методов расширения, которые могут быть вызваны для любой функции. Лучшее, что вы можете сделать, - это расширить сам класс Delegate, что, безусловно, также полезно, но не так сильно.

Обновление: см. Также Ответ Карга , иллюстрирующий разницу между анонимными делегатами и методами & лямбдами.

Обновление 2: Джеймс Харт делает важное, хотя и очень техническое замечание, что лямбды и делегаты не являются сущностями .NET (т. Е. В CLR нет понятия делегата или лямбды), а скорее рамки и языковые конструкции.

28 голосов
/ 16 сентября 2008

Вопрос немного двусмысленный, что объясняет большое расхождение в ответах, которые вы получаете.

Вы на самом деле спросили, в чем разница между лямбдами и делегатами в .NET Framework; это может быть одной из многих вещей. Вы спрашиваете:

  • В чем разница между лямбда-выражениями и анонимными делегатами на языке C # (или VB.NET)?

  • В чем разница между объектами System.Linq.Expressions.LambdaExpression и объектами System.Delegate в .NET 3.5?

  • Или что-то где-то между этими крайностями или вокруг них?

Некоторые люди, кажется, пытаются дать вам ответ на вопрос «в чем разница между лямбда-выражениями C # и .NET System.Delegate?», Который не имеет большого смысла.

.NET Framework сама по себе не понимает концепции анонимных делегатов, лямбда-выражений или замыканий - все это определяется языковыми спецификациями. Подумайте о том, как компилятор C # преобразует определение анонимного метода в метод в сгенерированном классе с переменными-членами для хранения состояния закрытия; в .NET нет ничего анонимного в делегате; это просто анонимно программисту C #, пишущему это. Это в равной степени относится и к лямбда-выражению, назначенному типу делегата.

Что понимает .NET DOES - это идея делегата - типа, который описывает сигнатуру метода, экземпляры которого представляют собой либо привязанные вызовы к определенным методам для определенных объектов, либо несвязанные вызовы для конкретного метода на конкретный тип, который может быть вызван для любого объекта этого типа, где указанный метод придерживается указанной подписи. Такие типы все наследуются от System.Delegate.

.NET 3.5 также представляет пространство имен System.Linq.Expressions, которое содержит классы для описания выражений кода - и которое также может представлять связанные или несвязанные вызовы методов для определенных типов или объектов. Затем экземпляры LambdaExpression можно скомпилировать в фактические делегаты (посредством чего генерируется код динамического метода, основанного на структуре выражения, и возвращается указатель делегата на него).

В C # вы можете создавать экземпляры типов System.Expressions.Expression, присваивая лямбда-выражение переменной указанного типа, что даст соответствующий код для построения выражения во время выполнения.

Конечно, если бы вы спросили, в чем разница между лямбда-выражениями и анонимными методами в C #, в конце концов, все это в значительной степени неуместно, и в этом случае основное отличие - это краткость, который склоняется к анонимным делегатам, когда вы не заботитесь о параметрах и не планируете возвращать значение, и к лямбдам, когда вы хотите ввести определяемые параметры и возвращаемые типы.

И лямбда-выражения поддерживают генерацию выражений.

18 голосов
/ 16 сентября 2008

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

public delegate string TestDelegate(int i);

public void Test(TestDelegate d)
{}

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

Test(delegate(int i) { return String.Empty; });
Test(delegate { return String.Empty; });
Test(i => String.Empty);
Test(D);

private string D(int i)
{
    return String.Empty;
}

Нельзя передать лямбда-выражение, которое не имеет параметров, или метод, который не имеет параметров. Это не разрешено:

Test(() => String.Empty); //Not allowed, lambda must match signature
Test(D2); //Not allowed, method must match signature

private string D2()
{
    return String.Empty;
}
13 голосов
/ 16 сентября 2008

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

3 голосов
/ 16 сентября 2008

У меня нет большого опыта в этом, но я бы сказал, что делегат является оберткой для любой функции, тогда как лямбда-выражение само по себе является анонимной функцией.

3 голосов
/ 16 сентября 2008

Делегат всегда является просто указателем на функцию. Лямбда может превращаться в делегат, но она также может превращаться в дерево выражений LINQ. Например,

Func<int, int> f = x => x + 1;
Expression<Func<int, int>> exprTree = x => x + 1;

Первая строка создает делегат, а вторая - дерево выражений.

2 голосов
/ 24 июня 2011

Совершенно очевидно, что подразумевался вопрос "в чем разница между лямбдами и анонимными делегатами?" Из всех ответов здесь только один человек понял это правильно - главное отличие в том, что лямбды можно использовать как для создания деревьев выражений, так и для делегатов.

Вы можете узнать больше на MSDN: http://msdn.microsoft.com/en-us/library/bb397687.aspx

2 голосов
/ 16 сентября 2008

Делегат - это ссылка на метод с определенным списком параметров и типом возвращаемого значения. Может включать или не включать объект.

Лямбда-выражение является формой анонимной функции.

2 голосов
/ 16 сентября 2008

Делегат является сигнатурой функции; что-то вроде

delegate string MyDelegate(int param1);

Делегат не реализует тело.

Лямбда - это вызов функции, который совпадает с подписью делегата. Для указанного выше делегата вы можете использовать любой из;

(int i) => i.ToString();
(int i) => "ignored i";
(int i) => "Step " + i.ToString() + " of 10";

Тип Delegate имеет неправильное имя; создание объекта типа Delegate фактически создает переменную, которая может содержать функции - будь то лямбда-выражения, статические методы или методы класса.

2 голосов
/ 16 сентября 2008

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

Это то же самое, я считаю:

Delegate delegate = x => "hi!";
Delegate delegate = delegate(object x) { return "hi";};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...