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


Відповіді:


140

Коротка відповідь: ні.

Більш довга відповідь, яка може не бути актуальною:

  • Якщо ви призначите лямбда типу делегата (наприклад, Funcабо Action), ви отримаєте анонімного делегата.
  • Якщо ви присвоїте лямбда типу Expression, ви отримаєте дерево вираження замість анонімного делегата. Потім дерево виразів може бути складено до анонімного делегата.

Редагувати: Ось декілька посилань для виразів.

  • System.Linq.Expression.Expression (TDelegate) (початок тут).
  • Пам'ять Linq з делегатами (наприклад, System.Func) використовує System.Linq.Enumerable . Linq to SQL (і все інше) з виразами використовує System.Linq.Queryable . Перевірте параметри цих методів.
  • Пояснення від ScottGu . Якщо коротко, то пам'ять Linq створить анонімні методи вирішення вашого запиту. Linq до SQL створить дерево вираження, яке представляє запит, а потім переведе це дерево в T-SQL. Linq to Entities створить дерево виразів, яке представляє запит, а потім переведе це дерево на відповідний платформі SQL.

3
Тип виразу? Це звучить як нова територія для мене. Де я можу дізнатися більше про типи виразів та використання дерев виразів у C #?
MojoFilter

2
Ще довша відповідь - є прискіпливі причини, чому вони також можуть бути конвертованими в різні типи делегатов :)
Джон Скіт

Зауважте, що лямбда може бути призначена лише типу Expression, якщо це вираз лямбда.
Micha Wiedenmann

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
Джон, ти роке. Еріку, щоб бути справжнім шанувальником Скету, ти повинен підписатися на його rss overflow rss, як я. Просто вставте stackoverflow.com/users/22656 у свій читач каналів.
Пол Батум

3
@RoyiNamir: Якщо ви використовуєте анонімний метод без списку параметрів, він сумісний з будь-яким типом делегата з параметрами невідмови / вимикання, доки тип повернення сумісний. В основному ви говорите "Мені не важливо параметрів". Зауважте, що delegate { ... }це не те саме, що delegate() { ... }- останній сумісний лише з типом делегата без параметрів.
Джон Скіт

2

У двох вище прикладах немає різниці, нуль.

Вираз:

() => { x = 0 }

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

() => { x = 0; } // Lambda statement body
() => x = 0      // Lambda expression body, could be an expression tree. 

6
Напевно, це означає, що є різниця "один складе, інший не буде";)
Джон Скіт

2

Емі В правильно. Зауважте, що використання дерев виразів може мати переваги. LINQ в SQL вивчить дерево виразів і перетворить його в SQL.

Ви також можете грати в трюки з ламдами та деревами виразів, щоб ефективно передавати імена членів класу до рамки безпечним способом рефакторингу. Мок - приклад цього.


-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< Student,bool> checkStudentAge = s => s.Age > someValue && s.Age < someValue ;

bool result = checkStudentAge ( Student Object);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.