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

Ответы:

140

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

Более длинный ответ, который может быть не актуален:

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

Изменить: Вот несколько ссылок для выражений.

  • System.Linq.Expression.Expression (TDelegate) (начать здесь).
  • Linq в памяти с делегатами (такими как System.Func) использует System.Linq.Enumerable . Linq to SQL (и все остальное) с выражениями использует System.Linq.Queryable . Проверьте параметры этих методов.
  • Объяснение от ScottGu . В двух словах, Linq in-memory создаст несколько анонимных методов для решения вашего запроса. Linq to SQL создаст дерево выражений, которое представляет запрос, а затем переведет это дерево в T-SQL. Linq to Entities создаст дерево выражений, которое представляет запрос, а затем переведет это дерево в соответствующий SQL платформу.
Эми Б
источник
3
Тип выражения? Это звучит как новая территория для меня. Где я могу узнать больше о типах выражений и использовании деревьев выражений в C #?
MojoFilter
2
Еще более длинный ответ - есть причудливые причины, по которым они также могут быть преобразованы в делегаты разных типов :)
Джон Скит,
Обратите внимание, что лямбда-выражение может быть назначено только типу выражения, если оно является лямбда-выражением.
Миха Виденманн
125

Мне нравится ответ Эми, но я думал, что буду педантичным. Вопрос говорит: «После того, как он составлен» - что говорит о том , что оба выражения было скомпилированы. Как они оба могут скомпилироваться, но с одним преобразованным в делегат, а другим в дерево выражений? Это сложный вопрос - вы должны использовать другую функцию анонимных методов; единственный, который не разделяется лямбда-выражениями. Если указать анонимный метод без указания списка параметров на все он совместит с любым типом делегата возвращения недействительным и без каких - либо 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; } );
    }
}

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

Джон Скит
источник
9
Я достал свой попкорн и прочитал все это. Это то различие, о котором я, вероятно, никогда бы не подумал, даже если бы смотрел прямо в лицо.
MojoFilter
27
Я знал кое-что из этого, но я должен поздравить вас с вашей способностью сообщать об этом людям.
Эми Б
1
Для всех, кто интересуется изменениями в .NET 4.0 (на основе CTP) - см. Marcgravell.blogspot.com/2008/11/future-expressions.html . Обратите внимание, что C # 4.0 пока что не делает ничего нового, насколько я могу судить.
Марк Гравелл
4
Джон, ты молодец Эрик, чтобы быть настоящим фанатом Skeet, ты должен быть подписан на его переполнение стека rss, как и я. Просто вставьте stackoverflow.com/users/22656 в свою программу чтения каналов.
Пол Батум
3
@RoyiNamir: Если вы используете анонимный метод без списка параметров, он совместим с любым типом делегата с параметрами не-ref / out, при условии, что возвращаемый тип совместим. По сути, вы говорите: «Меня не волнуют параметры». Обратите внимание, что delegate { ... }это не то же самое, delegate() { ... }что последний совместим только с типом делегата без параметров.
Джон Скит
2

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

Выражение:

() => { x = 0 }

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

() => { x = 0; } // Lambda statement body
() => x = 0      // Lambda expression body, could be an expression tree. 
Olmo
источник
6
Конечно, это означает, что есть разница «один скомпилирует, а другой нет»;)
Джон Скит
2

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

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

Даниэль Плейстед
источник
-1

Есть разница

Пример:

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));
});
Ан Бинь Тронг
источник
Неправильная лямбда не удалась только потому, что подпись параметра метода не совпадает
Джек Ван
-1

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

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

(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);
Йогеш Праджапати
источник