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

StackOverflow https://stackoverflow.com/questions/73227

  •  09-06-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

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

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

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

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

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;

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

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

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

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

Другие советы

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

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

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

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

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

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

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

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

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

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

Конечно, если вы были в конце концов, если спросить, в чем разница между лямбда-выражениями и анонимными методами в 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";

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

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

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

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

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

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

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

Итак, хотя кто-то может сказать, что lambda - это синтаксический сахар для делегатов, я бы сказал, что делегаты - это мост для облегчения работы людей с лямбдами в 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, используя делегаты, анонимные делегаты и 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 #.

А (Нормальный, в противовес так называемому общий делегаты, см . позже) делегат следует рассматривать как разновидность c ++ 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 была бы функцией, принимающей 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 это функция, принимающая в качестве аргумента и возвращающая double.

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

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

(double x) => x * x является выражение.Это не тип, в то время как делегаты (универсальные или нет) являются таковыми.

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

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

Это анонимный метод -

(string testString) => { Console.WriteLine(testString); };

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

delegate void PrintTestString(string testString); // declare a delegate

PrintTestString print = (string testString) => { Console.WriteLine(testString); }; 
print();

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

s => s.Age > someValue && s.Age < someValue    // will return true/false

Мы можем использовать делегат функции для использования этого выражения.

Func< Student,bool> checkStudentAge = s => s.Age > someValue && s.Age < someValue ;

bool result = checkStudentAge ( Student Object);

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top