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

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

<p>на самом деле это две совершенно разные вещи. «делегат» - это имя переменной, которая содержит ссылку на метод или лямбду, а лямбда - это метод без постоянного имени.</p><p>лямбды очень похожи на другие методы, за исключением пары тонких отличий.</p><ol> <li>обычный метод определен в <a href="http://en.wikipedia.org/wiki/statement\_(programming)">«заявлении»</a> и привязан к постоянному имени, тогда как лямбда определяется «на лету» в <a href="http://en.wikipedia.org/wiki/expression\_(programming)">«выражении»</a> и не имеет постоянного имени.</li> <li>некоторые лямбда-выражения можно использовать с деревьями выражений .net, а методы - нет.</li> </ol><p>делегат определяется следующим образом:</p><pre><code>delegate int32 binaryintop(int32 x, int32 y); </code></pre><p>переменной типа binaryintop может быть назначен либо метод, либо labmda, если сигнатура одинакова: два аргумента int32 и возврат int32.</p><p>лямбда может быть определена так:</p><pre><code>binaryintop sumofsquares = (a, b) => a*a + b*b; </code></pre><p>следует также отметить, что, хотя общие типы func и action часто рассматриваются как «лямбда-типы», они аналогичны любым другим делегатам. приятная вещь о них заключается в том, что они по существу определяют имя для любого типа делегата, который вам может понадобиться (до 4 параметров, хотя вы, конечно, можете добавить и свой собственный). поэтому, если вы используете широкий спектр типов делегатов, но не более одного раза, вы можете избежать загромождения своего кода объявлениями делегатов с помощью func и action.</p><p>вот иллюстрация того, как func и action «не только для лямбд»:</p><pre><code>int32 diffofsquares(int32 x, int32 y) { return x*x - y*y; } func<int32, int32, int32> funcptr = diffofsquares; </code></pre><p>еще одна полезная вещь, которую нужно знать, это то, что типы делегатов (не сами методы) с одинаковой сигнатурой, но разными именами не будут неявно приводиться друг к другу. это включает в себя делегатов func и action. однако, если подпись идентична, вы можете явно привести между ними.</p><p>пройдя лишнюю милю .... в c # функции гибки, с использованием лямбд и делегатов. но в c # нет «первоклассных функций». вы можете использовать имя функции, назначенное переменной-делегату, чтобы по существу создать объект, представляющий эту функцию. но это действительно трюк с компилятором. если вы начнете оператор, написав имя функции, за которым следует точка (то есть попытайтесь получить доступ к элементам самой функции), вы обнаружите, что там нет членов, на которые можно ссылаться. даже те, что из object. это не позволяет программисту делать полезные (и потенциально опасные) вещи, такие как добавление методов расширения, которые могут быть вызваны для любой функции. лучшее, что вы можете сделать, - это расширить сам класс delegate, что, безусловно, также полезно, но не так сильно.</p><p>обновление: также см. <a href="https://stackoverflow.com/questions/73227/what-is-the-difference-between-lambdas-and-delegates-in-the-net-framework#73448">ответ карга</a> иллюстрирующий разницу между анонимными делегатами и методами & лямбдами.</p><p>обновление 2: <a href="https://stackoverflow.com/questions/73227/what-is-the-difference-between-lambdas-and-delegates-in-the-net-framework#74414">джеймс харт</a> делает важное, хотя и очень техническое замечание, что лямбды и делегаты не являются сущностями .net (т. е. clr не имеет понятия делегата или лямбды), а скорее являются фреймворком и языковыми конструкциями.</p>

<p>вопрос немного двусмысленный, что объясняет большое расхождение в ответах, которые вы получаете.</p><p>вы действительно спросили, в чем разница между лямбдами и делегатами в .net framework; это может быть одной из многих вещей. вы спрашиваете:</p><ul> <li><p>в чем разница между лямбда-выражениями и анонимными делегатами в языке c # (или vb.net)?</p></li> <li><p>в чем разница между объектами system.linq.expressions.lambdaexpression и объектами system.delegate в .net 3.5?</p></li> <li><p>или что-то между этими крайностями или вокруг них?</p></li> </ul><p>некоторые люди, кажется, пытаются дать вам ответ на вопрос «в чем разница между лямбда-выражениями c # и .net system.delegate?», который не имеет большого смысла.</p><p>платформа .net сама по себе не понимает концепции анонимных делегатов, лямбда-выражений или замыканий - все это определяется языковыми спецификациями. подумайте о том, как компилятор c # преобразует определение анонимного метода в метод в сгенерированном классе с переменными-членами для хранения состояния закрытия; в .net нет ничего анонимного в делегате; это просто анонимно программисту c #, пишущему это. это в равной степени относится и к лямбда-выражению, назначенному типу делегата.</p><p>.net<em>делает</em>понимает идею делегата - типа, который описывает сигнатуру метода, экземпляры которого представляют собой либо связанные вызовы определенных методов для определенных объектов, либо несвязанные вызовы определенного метода для конкретного тип, который может быть вызван для любого объекта этого типа, где указанный метод придерживается указанной подписи. такие типы все наследуются от system.delegate.</p><p>.net 3.5 также представляет пространство имен system.linq.expressions, которое содержит классы для описания выражений кода, и которое также может представлять связанные или несвязанные вызовы методов для определенных типов или объектов. затем экземпляры lambdaexpression могут быть скомпилированы в фактические делегаты (посредством чего генерируется код динамического метода, основанного на структуре выражения, и возвращается указатель делегата на него).</p><p>в c # вы можете создавать экземпляры типов system.expressions.expression, присваивая лямбда-выражение переменной указанного типа, что создаст соответствующий код для построения выражения во время выполнения.</p><p>конечно, если бы вы<em> были </em>спросили, в чем разница между лямбда-выражениями и анонимными методами в c #, в конце концов, тогда все это в значительной степени неуместно, и в этом случае основное отличие - это краткость, которая склоняется к анонимным делегатам когда вы не заботитесь о параметрах и не планируете возвращать значение, и к лямбдам, когда вы хотите ввести определяемые параметры и возвращаемые типы.</p><p>и лямбда-выражения поддерживают генерацию выражений.</p>

<p>одно из отличий состоит в том, что анонимный делегат может опускать параметры, тогда как лямбда должна соответствовать точной сигнатуре. дано:</p><pre><code>public delegate string testdelegate(int i); public void test(testdelegate d) {} </code></pre><p>вы можете вызвать его следующими четырьмя способами (обратите внимание, что во второй строке есть анонимный делегат без параметров):</p><pre><code>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; } </code></pre><p>вы не можете передать лямбда-выражение, которое не имеет параметров, или метод, который не имеет параметров. это не разрешено:</p><pre><code>test(() => string.empty); //not allowed, lambda must match signature test(d2); //not allowed, method must match signature private string d2() { return string.empty; } </code></pre>

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

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

<p>делегат всегда является просто указателем на функцию. лямбда может превращаться в делегат, но она также может превращаться в дерево выражений linq. например,</p><pre><code>func<int, int> f = x => x + 1; expression<func<int, int>> exprtree = x => x + 1; </code></pre><p>первая строка создает делегат, а вторая - дерево выражений.</p>

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

<p>лямбды - это просто синтаксический сахар для делегата. компилятор в итоге преобразует лямбды в делегаты.</p><p>это то же самое, я считаю</p><pre><code>delegate delegate = x => "hi!"; delegate delegate = delegate(object x) { return "hi";}; </code></pre>

<p>делегат является сигнатурой функции; что-то вроде </p><pre><code>delegate string mydelegate(int param1); </code></pre><p>делегат не реализует тело. </p><p>лямбда - это вызов функции, который совпадает с подписью делегата. для вышеуказанного делегата вы можете использовать любой из;</p><pre><code>(int i) => i.tostring(); (int i) => "ignored i"; (int i) => "step " + i.tostring() + " of 10"; </code></pre><p>однако тип <code>delegate</code> имеет плохое имя; создание объекта типа <code>delegate</code> фактически создает переменную, которая может содержать функции - будь то лямбда-выражения, статические методы или методы класса.</p>

<p>совершенно очевидно, что вопрос должен был звучать так: «в чем разница между лямбдами и <strong>anonymous</strong> делегатами?» из всех ответов здесь только один человек понял это правильно - главное отличие в том, что лямбды могут использоваться как для создания деревьев выражений, так и для делегатов.</p><p>вы можете прочитать больше на msdn: <a href="http://msdn.microsoft.com/en-us/library/bb397687.aspx">http://msdn.microsoft.com/en-us/library/bb397687.aspx</a> </p>

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

<p>делегаты - это просто структурная типизация функций. вы могли бы сделать то же самое с номинальной типизацией и реализацией анонимного класса, который реализует интерфейс или абстрактный класс, но в итоге получается много кода, когда требуется только одна функция.</p><p>лямбда происходит от идеи лямбда-исчисления церкви алонзо в 1930-х годах. это анонимный способ создания функций. они становятся особенно полезными для составления функций</p><p>поэтому, хотя некоторые могут сказать, что лямбда - это синтаксический сахар для делегатов, я бы сказал, что делегаты - это мост, облегчающий переход людей на лямбды в c #.</p>

<p><em>я предполагаю, что ваш вопрос касается c #, а не .net, из-за неоднозначности вашего вопроса, поскольку .net не остается в одиночестве - то есть без c # - понимания делегатов и лямбда-выражений.</em></p><p>делегат (<em>normal</em>, в отличие от так называемых делегатов <em>generic</em>, позже <em>cf</em>) следует рассматривать как разновидность c ++ <code>typedef</code> типа указателя на функцию, например в c ++:</p><pre><code>r (*thefunctionpointer) ( t ) ; </code></pre><p>typedef - это тип <code>thefunctionpointer</code>, который является типом указателей на функцию, принимающую объект типа <code>t</code> и возвращающий объект типа <code>r</code>. вы бы использовали это так:</p><pre><code>thefunctionpointer = &thefunction ; r r = (*thefunctionpointer) ( t ) ; // where t is of type t </code></pre><p>где <code>thefunction</code> будет функцией, принимающей <code>t</code> и возвращающей <code>r</code>.</p><p>в c # вы бы пошли на</p><pre><code>delegate r thedelegate( t t ) ; // and yes, here the identifier t is needed </code></pre><p>и вы бы использовали это так:</p><pre><code>thedelegate thedel = thefunction ; r r = thedel ( t ) ; // where t is of type t </code></pre><p>где <code>thefunction</code> будет функцией, принимающей <code>t</code> и возвращающей <code>r</code>. это для делегатов, так называемых нормальных делегатов.</p><p>теперь у вас также есть обобщенные делегаты в c #, которые являются обобщенными делегатами, то есть <em>i.e.</em>, которые, так сказать, «шаблонизированы», используя при этом выражение c ++. они определены так:</p><pre><code>public delegate tresult func<in t, out tresult>(t arg); </code></pre><p>и вы можете использовать их так:</p><pre><code>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); </code></pre><p>где <code>thefunction2</code> - это функция, принимающая в качестве аргумента и возвращающая <code>double</code>.</p><p>теперь представьте, что вместо <code>thefunction2</code> я хотел бы использовать «функцию», которая пока нигде не определена оператором, и которую я никогда не буду использовать позже. тогда c # позволяет нам использовать <em>выражение</em> этой функции. под выражением я подразумеваю его «математическое» (или функциональное, чтобы придерживаться программ) выражение, например: <code>double x</code> я буду <em>ассоциировать</em> <code>double</code> <code>x*x</code>. в математике вы пишете это, используя <a href="https://math.stackexchange.com/questions/651205/regarding-the-notation-f-a-mapsto-b">латексный символ "\ mapsto"</a> . в c # функциональная нотация была заимствована: <code>=></code>. например :</p><pre><code>func<double, double> thefunctor = ( (double x) => x * x ); // outer brackets are not // mandatory </code></pre><p><code>(double x) => x * x</code> является <a href="http://en.wikipedia.org/wiki/expression\_(computer\_science)"><em>выражением</em></a> . это не тип, тогда как делегаты (общие или нет).</p><p>мораль? в конце концов, что такое делегат (соответственно универсальный делегат), если не тип указателя функции (соответственно упакованный + умный + тип указателя универсальной функции), а? что-то другое ! смотрите <a href="http://blog.monstuff.com/archives/000037.html">это</a> и <a href="http://msdn.microsoft.com/fr-fr/library/system.delegate%28v=vs.110%29.aspx">это</a> .</p>

<p>вот пример, который я выложил некоторое время на своем хромом блоге. скажем, вы хотите обновить метку из рабочего потока. у меня есть 4 примера того, как обновить эту метку с 1 до 50, используя делегаты, анон делегаты и 2 типа лямбд.</p><pre><code> 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); } } </code></pre>

<p>некоторые основные здесь . «делегат» на самом деле имя переменной, которая содержит ссылку на метод или лямбду</p><p>это анонимный метод - (string teststring) => {console.writeline (teststring); };</p><p>поскольку у анонимного метода нет никакого имени, нам нужен делегат, в котором мы можем назначить оба этих метода или выражения. например.</p><p>делегат void printteststring (строка teststring); // объявляем делегата</p><p>printteststring print = (string teststring) => {console.writeline (teststring); }; распечатать();</p><hr><p>то же самое с лямбда-выражением. обычно нам нужен делегат, чтобы использовать их</p><p>s => s.age> somevalue && s.age <somevalue // вернет true/false</p><p>мы можем использовать делегат func, чтобы использовать это выражение.</p><p>func <student, bool> checkstudentage = s => s.age> somevalue && s.age <somevalue;</p><p>bool result = checkstudentage (student object);</p>

<p>лямбды - это упрощенные версии делегатов. они имеют некоторые свойства <a href="http://en.wikipedia.org/wiki/closure\_(computer\_science)">closure</a> как анонимные делегаты, но также позволяют использовать неявную типизацию. лямбда, как это:</p><pre><code>something.sort((x, y) => return x.compareto(y)); </code></pre><p>гораздо более кратким, чем то, что вы можете сделать с делегатом:</p><pre><code>something.sort(sortmethod); ... private int sortmethod(sometype one, sometype two) { one.compareto(two) } </code></pre>