В чем разница между лямбдами и делегатами в the.NET рамки?


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

17 61

17 ответов:

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

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

  1. нормальный метод определяется в "Ведомости" и привязан к постоянному имени, тогда как лямбда определяется "на лету" в "выражение" и не имеет постоянного имени.
  2. некоторые лямбды могут использоваться с деревьями выражений .NET, тогда как методы не могут.

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

delegate Int32 BinaryIntOp(Int32 x, Int32 y);

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

лямбда может быть определена следующим образом:

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

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

вот иллюстрация того, как Функция и действие "не только для лямбд":

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

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

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

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

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

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

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

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

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

  • в чем разница между Система.В LINQ.Выражения.Объекты и система лямбда-выражения.Делегировать объекты в .NET 3.5?

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

некоторые люди, кажется, пытаются дать вам ответ на вопрос " в чем разница между лямбда-выражениями C# и системой .NET.Делегат?- в этом нет никакого смысла.

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

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

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

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

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

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

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

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;
}

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

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

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

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

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

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

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

Delegate delegate = x => "hi!";
Delegate delegate = delegate(object x) { return "hi";};

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

delegate string MyDelegate(int param1);

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

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

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

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

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

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

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

вы можете прочитать больше на MSDN:http://msdn.microsoft.com/en-us/library/bb397687.aspx

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

лямбда исходит из идеи лямбда-исчисления Церкви Алонсо в 1930-х гг. это анонимный способ создания функций. Они становятся особенно полезными для составления функций

Так что пока некоторые можно сказать, что лямбда-это синтаксический сахар для делегатов, я бы сказал, что делегаты-это мост для облегчения людей в лямбды в c#.

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

вы можете получить делегат, который указывает на лямбда-выражение в качестве метода, приведя его в делегат, или если передать его в качестве параметра в метод, который ожидает определенного типа делегата компилятор будет приводить его для вас. Используя его внутри оператора LINQ, лямбда будет переведена компилятором в дерево выражений вместо простого делегата.

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

лямбды-это упрощенные версии делегатов. Они имеют некоторые из свойств закрытие как анонимные делегаты,но также позволяют использовать подразумеваемый ввод. Лямбда вроде этого:

something.Sort((x, y) => return x.CompareTo(y));

намного более лаконично, чем то, что вы можете сделать с делегатом:

something.Sort(sortMethod);
...

private int sortMethod(SomeType one, SomeType two)
{
    one.CompareTo(two)
}

вот пример, который я поставил некоторое время на моем хромом блоге. Допустим, вы хотите обновить метку из рабочего потока. У меня есть 4 примера того, как обновить эту метку от 1 до 50 с помощью делегатов, делегатов anon и 2 типов лямбд.

 private void button2_Click(object sender, EventArgs e) 
     { 
         BackgroundWorker worker = new BackgroundWorker(); 
         worker.DoWork += new DoWorkEventHandler(worker_DoWork); 
         worker.RunWorkerAsync(); 
     } 

     private delegate void UpdateProgDelegate(int count); 
     private void UpdateText(int count) 
     { 
         if (this.lblTest.InvokeRequired) 
         { 
             UpdateProgDelegate updateCallBack = new UpdateProgDelegate(UpdateText); 
             this.Invoke(updateCallBack, new object[] { count }); 
         } 
         else 
         { 
             lblTest.Text = count.ToString(); 
         } 
     } 

     void worker_DoWork(object sender, DoWorkEventArgs e) 
     {   
         /* Old Skool delegate usage.  See above for delegate and method definitions */ 
         for (int i = 0; i < 50; i++) 
         { 
             UpdateText(i); 
             Thread.Sleep(50); 
         } 

         // Anonymous Method 
         for (int i = 0; i < 50; i++) 
         { 
             lblTest.Invoke((MethodInvoker)(delegate() 
             { 
                 lblTest.Text = i.ToString(); 
             })); 
             Thread.Sleep(50); 
         } 

         /* Lambda using the new Func delegate. This lets us take in an int and 
          * return a string.  The last parameter is the return type. so 
          * So Func<int, string, double> would take in an int and a string 
          * and return a double.  count is our int parameter.*/ 
         Func<int, string> UpdateProgress = (count) => lblTest.Text = count.ToString(); 
         for (int i = 0; i < 50; i++) 
         { 
             lblTest.Invoke(UpdateProgress, i); 
             Thread.Sleep(50); 
         } 

         /* Finally we have a totally inline Lambda using the Action delegate 
          * Action is more or less the same as Func but it returns void. We could 
          * use it with parameters if we wanted to like this: 
          * Action<string> UpdateProgress = (count) => lblT…*/ 
         for (int i = 0; i < 50; i++) 
         { 
             lblTest.Invoke((Action)(() => lblTest.Text = i.ToString())); 
             Thread.Sleep(50); 
         } 
     }

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

A (нормальный, в противовес так называемому generic делегатов, cf позже) делегат должен рассматриваться как своего рода с++ typedef типа указателя функции, например в c++ :

R (*thefunctionpointer) ( T ) ;

typedef для типа thefunctionpointer что то типа указателей на функцию, принимающую объект типа T и возвращает объект типа R. Вы бы использовали его так:

thefunctionpointer = &thefunction ;
R r = (*thefunctionpointer) ( t ) ; // where t is of type T

здесь thefunction была бы функция, принимающая T и возврат R.

в C# вы бы пошли на

delegate R thedelegate( T t ) ; // and yes, here the identifier t is needed

и вы бы использовали его так:

thedelegate thedel = thefunction ;
R r = thedel ( t ) ; // where t is of type T

здесь thefunction была бы функция принимая a T и возврат R. Это для делегатов, так называемых нормальных представителей.

теперь у вас также есть общие делегаты в c#, которые являются делегатами, которые являются общими,т. е. которые, так сказать," шаблонны", используя тем самым выражение c++. Они определяются следующим образом :

public delegate TResult Func<in T, out TResult>(T arg);

и вы можете использовать их так:

Func<double, double> thefunctor = thefunction2; // call it a functor because it is
                                                // really as a functor that you should
                                                // "see" it
double y = thefunctor(2.0);

здесь thefunction2 - это функция, принимающая в качестве аргумента и возвращающая a double.

теперь представьте себе, что вместо thefunction2 я хотел бы использовать "функцию", которая нигде не определена на данный момент, с помощью оператора, и которую я никогда не буду использовать позже. Тогда c# позволяет нам использовать выражение этой функции. Под выражением я подразумеваю "математическое" (или функциональное, чтобы придерживаться программ) выражение его, например: to a double x я связать the doublex*x. В математике вы пишете это с помощью символ латекса "\mapsto". В c# the функциональная нотация была заимствована : =>. Например :

Func<double, double> thefunctor = ( (double x) => x * x ); // outer brackets are not
                                                           // mandatory

(double x) => x * x это выражение. Это не тип, а делегаты (общие или нет).

мораль ? В конце концов, что такое делегат (ОТВ. универсальный делегат), если не тип указателя функции (ОТВ. обернутый + умный + общий тип указателя функции), да ? Что-то еще ! Смотрите этой и это.

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

Это анонимный метод - (string testString) = > {консоль.WriteLine (testString);};

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

делегировать void PrintTestString (string testString); / / объявить делегат

PrintTestString печать = (строка проверка строка) => { консоли.WriteLine(testString); }; print ();


то же самое с лямбда-выражением. Обычно нам нужен делегат, чтобы использовать их

с => С. Возраст > некоторое_значение && ы.Возраст

мы можем использовать делегат func для использования этого выражения.

функции checkStudentAge = ы = ы.Возраст > некоторое_значение && ы.Возраст

bool result = checkStudentAge (студент Объект);

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