ключевое слово делегата против лямбда-нотации - PullRequest
173 голосов
/ 18 ноября 2008

Как только он скомпилирован, есть ли разница между:

delegate { x = 0; }

и

() => { x = 0 }

Ответы [ 6 ]

136 голосов
/ 18 ноября 2008

Краткий ответ: нет.

Более длинный ответ, который может не относиться к теме:

  • Если вы назначите лямбду типу делегата (например, Func или Action), вы получите анонимного делегата.
  • Если вы назначите лямбду типу Expression, вы получите дерево выражений вместо анонимного делегата. Затем дерево выражений может быть скомпилировано с анонимным делегатом.

Edit: Вот несколько ссылок на выражения.

  • System.Linq.Expression.Expression (TDelegate) (начать здесь).
  • Linq в памяти с делегатами (такими как System.Func) использует System.Linq.Enumerable . Linq to SQL (и все остальное) с выражениями использует System.Linq.Queryable . Проверьте параметры этих методов.
  • An Объяснение от ScottGu . В двух словах, Linq in-memory создаст несколько анонимных методов для решения вашего запроса. Linq to SQL создаст дерево выражений, которое представляет запрос, а затем переведет это дерево в T-SQL. Linq to Entities создаст дерево выражений, которое представляет запрос, а затем переведет это дерево в соответствующий SQL платформу.
121 голосов
/ 18 ноября 2008

Мне нравится ответ Дэвида, но я думал, что буду педантичным. Вопрос говорит: «После того, как он скомпилирован» - это говорит о том, что оба выражения имеют скомпилировано. Как они оба могут скомпилироваться, но если один преобразовать в делегат, а другой в дерево выражений? Это сложно - вам нужно использовать другую функцию анонимных методов; единственный, который не разделяется лямбда-выражениями. Если вы указываете анонимный метод без указания списка параметров для всех , он совместим с любым типом делегата, возвращающим void, и без каких-либо параметров out. Вооружившись этим знанием, мы должны иметь возможность создать две перегрузки, чтобы сделать выражения совершенно однозначными, но очень разными.

Но беда поражает! По крайней мере, в C # 3.0 вы не можете преобразовать лямбда-выражение с телом блока в выражение, равно как вы не можете преобразовать лямбда-выражение с присваиванием в теле (даже если оно используется в качестве возвращаемого значения). Это может измениться в C # 4.0 и .NET 4.0, которые позволяют выразить больше в дереве выражений. Другими словами, с примерами, которые MojoFilter дал, эти два будут почти всегда преобразовываться в одно и то же. (Подробнее через минуту.)

Мы можем использовать трюк с параметрами делегата, если немного изменим тела:

using System;
using System.Linq.Expressions;

public class Test
{
    static void Main()
    {
        int x = 0;
        Foo( () => x );
        Foo( delegate { return x; } );
    }

    static void Foo(Func<int, int> action)
    {
        Console.WriteLine("I suspect the anonymous method...");
    }

    static void Foo(Expression<Func<int>> func)
    {
        Console.WriteLine("I suspect the lambda expression...");
    }
}

Но подождите! Мы можем различать эти два, даже не используя деревья выражений, если мы достаточно хитры. В приведенном ниже примере используются правила разрешения перегрузки (и трюк сопоставления анонимного делегата) ...

using System;
using System.Linq.Expressions;

public class Base
{
    public void Foo(Action action)
    {
        Console.WriteLine("I suspect the lambda expression...");
    }
}

public class Derived : Base
{
    public void Foo(Action<int> action)
    {
        Console.WriteLine("I suspect the anonymous method...");
    }
}

class Test
{
    static void Main()
    {
        Derived d = new Derived();
        int x = 0;
        d.Foo( () => { x = 0; } );
        d.Foo( delegate { x = 0; } );
    }
}

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

2 голосов
/ 18 ноября 2008

Дэвид Б прав. Обратите внимание, что могут быть преимущества использования деревьев выражений. LINQ to SQL проверит дерево выражений и преобразует его в SQL.

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

2 голосов
/ 18 ноября 2008

В двух приведенных выше примерах нет разницы, ноль.

Выражение:

() => { x = 0 }

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

ему нужна точка с запятой
() => { x = 0; } // Lambda statement body
() => x = 0      // Lambda expression body, could be an expression tree. 
0 голосов
/ 13 мая 2018

Некоторые основы здесь.

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

(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 для использования этого выражения.

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

bool result = checkStudentAge ( Student Object);
0 голосов
/ 25 мая 2012

Есть разница

Пример:

var mytask = Task.Factory.StartNew(() =>
{
    Thread.Sleep(5000);
    return 2712;
});
mytask.ContinueWith(delegate
{
    _backgroundTask.ContinueTask(() =>lblPercent.Content = mytask.Result.ToString(CultureInfo.InvariantCulture));
});   

И я заменяю на лямбду: (ошибка)

var mytask = Task.Factory.StartNew(() =>
{
    Thread.Sleep(5000);
    return 2712;
});
mytask.ContinueWith(()=>
{
    _backgroundTask.ContinueTask(() =>lblPercent.Content = mytask.Result.ToString(CultureInfo.InvariantCulture));
});
...