Подтвердить что ты не робот

В чем разница между lambdas и делегатами в .NET Framework?

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

4b9b3361

Ответ 1

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

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

  • Обычный метод определяется в "statement" и привязан к постоянному имени, тогда как lambda определяется "на лету" в "выражение" и не имеет постоянного имени.
  • Некоторые lambdas могут использоваться с деревьями выражений .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. Однако, если подпись идентична, вы можете явно указать между ними.

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

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

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

Ответ 2

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

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

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

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

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

Некоторые люди, похоже, пытаются дать вам ответ на вопрос: "В чем разница между выражениями С# Lambda и .NET System.Delegate?", что не имеет большого смысла.

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

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

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

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

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

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

Ответ 3

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

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

Ответ 4

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

Ответ 5

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

Ответ 6

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

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

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

Ответ 7

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

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

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

Ответ 8

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

delegate string MyDelegate(int param1);

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

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

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

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

Ответ 9

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

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

Ответ 10

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

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

Ответ 11

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

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

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

Ответ 12

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

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

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

Ответ 13

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

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

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

something.Sort(sortMethod);
...

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

Ответ 14

Вот пример, который я немного притворил в своем хромом блоге. Предположим, вы хотели обновить ярлык из рабочего потока. У меня есть 4 примера обновления этого ярлыка от 1 до 50 с помощью делегатов, анонимных делегатов и 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); 
         } 
     }

Ответ 15

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

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

R (*thefunctionpointer) ( T ) ;

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

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

где thefunction будет функцией, принимающей T и возвращающей R.

В С# вы отправитесь за

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 будет функцией, принимающей T и возвращающей R. Это для делегатов, так называемых нормальных делегатов.

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

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 я хотел бы использовать "функцию", которая пока не определена, с помощью инструкции, и что я никогда не буду использовать ее позже. Тогда С# позволяет использовать выражение этой функции. Под выражением я подразумеваю выражение "математическое" (или функциональное, чтобы придерживаться программ), например: до double x я свяжусь с double x*x. В математике вы пишете это, используя символ "\mapsto" латекс. В С# была записана функциональная нотация: =>. Например:

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

(double x) => x * x является expression. Это не тип, тогда как делегаты (общие или нет).

Мораль? В конце концов, что такое делегат (или общий делегат), если не тип указателя функции (например, тип wrapped + smart + generic function pointer type), да? Что-то другое! См. this и который.

Ответ 16

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