deleguj słowo kluczowe vs. notacja lambda

Odpowiedzi:

140

Krótka odpowiedź: nie.

Dłuższa odpowiedź, która może nie być istotna:

  • Jeśli przypiszesz lambda do typu delegata (takiego jak Funclub Action), otrzymasz anonimowego delegata.
  • Jeśli przypiszesz lambda do typu wyrażenia, zamiast anonimowego delegata otrzymasz drzewo wyrażeń. Drzewo wyrażeń można następnie skompilować do anonimowego uczestnika.

Edycja: Oto kilka linków do wyrażeń.

  • System.Linq.Expression.Expression (TDelegate) (zacznij tutaj).
  • Linq w pamięci z delegatami (takimi jak System.Func) używa System.Linq.Enumerable . Linq na SQL (i cokolwiek innego) z wyrażeniami używa System.Linq.Queryable . Sprawdź parametry tych metod.
  • Wyjaśnienie od ScottGu . Krótko mówiąc, w pamięci Linq w pamięci powstanie anonimowa metoda rozwiązania twojego zapytania. Linq na SQL utworzy drzewo wyrażeń, które reprezentuje zapytanie, a następnie przetłumaczy to drzewo na T-SQL. Linq to Entities utworzy drzewo wyrażeń, które reprezentuje zapytanie, a następnie przetłumaczy je na odpowiedni SQL na platformie.
Amy B.
źródło
3
Typ wyrażenia? Dla mnie to brzmi jak nowe terytorium. Gdzie mogę dowiedzieć się więcej o typach wyrażeń i korzystaniu z drzew wyrażeń w języku C #?
MojoFilter,
2
Jeszcze dłuższa odpowiedź - istnieją dziwne powody, dla których można je również konwertować na różne typy delegatów :)
Jon Skeet,
Zauważ, że lambda można przypisać do typu wyrażenia tylko wtedy, gdy jest to wyrażenie lambda.
Micha Wiedenmann
125

Podoba mi się odpowiedź Amy, ale myślałem, że będę pedantyczny. Pytanie mówi: „Po to jest skompilowany” - co sugeruje, że oba wyrażenia nie został opracowany. Jak oba można skompilować, ale jeden z nich jest konwertowany na delegata, a drugi na drzewo wyrażeń? To trudne - musisz użyć innej funkcji anonimowych metod; jedyny, który nie jest współdzielony przez wyrażenia lambda. Jeśli określisz sposób anonimowy bez podania listy parametrów w ogóle nie jest kompatybilny z dowolnym typem delegat powrocie nieważne i bez żadnych outparametrów. Uzbrojeni w tę wiedzę powinniśmy być w stanie skonstruować dwa przeciążenia, aby wyrażenia były całkowicie jednoznaczne, ale bardzo różne.

Ale katastrofa uderza! Przynajmniej w języku C # 3.0 nie można przekonwertować wyrażenia lambda z treścią bloku na wyrażenie - nie można też przekonwertować wyrażenia lambda z przypisaniem w treści (nawet jeśli jest ono używane jako wartość zwracana). Może się to zmienić w C # 4.0 i .NET 4.0, które pozwalają wyrazić więcej w drzewie wyrażeń. Innymi słowy, z przykładami podanymi przez MojoFilter, te dwa prawie zawsze zostaną przekonwertowane na to samo. (Więcej szczegółów za minutę.)

Możemy użyć sztuczki delegowania parametrów, jeśli zmienimy nieco ciała:

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...");
    }
}

Ale poczekaj! Możemy je rozróżnić nawet bez użycia drzewek wyrażeń, jeśli jesteśmy wystarczająco przebiegli. W poniższym przykładzie użyto reguł rozwiązywania problemów z przeciążeniem (i anonimowej sztuczki polegającej na dopasowaniu pełnomocnika) ...

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; } );
    }
}

Auć. Pamiętajcie, dzieciaki, za każdym razem, gdy przeładujecie metodę odziedziczoną z klasy podstawowej, mały kotek zaczyna płakać.

Jon Skeet
źródło
9
Wyjąłem swój popcorn i przeczytałem całość. To pewne rozróżnienie, o którym pewnie nigdy bym nie pomyślał, nawet gdybym patrzył mu prosto w twarz.
MojoFilter,
27
Wiedziałem trochę o tym, ale muszę pogratulować twojej zdolności do przekazywania tego ludziom.
Amy B,
1
Dla wszystkich zainteresowanych zmianami w .NET 4.0 (opartym na CTP) - zobacz marcgravell.blogspot.com/2008/11/future-expressions.html . Zauważ, że C # 4.0 nie robi nic nowego, o ile wiem.
Marc Gravell
4
Jon, ty rządzisz. Erik, aby być prawdziwym fanboi Skeeta, powinieneś subskrybować jego przepełnienie stosu rss tak jak ja. Po prostu włóż stackoverflow.com/users/22656 do czytnika kanałów.
Paul Batum,
3
@RoyiNamir: Jeśli używasz anonimowej metody bez listy parametrów, jest ona kompatybilna z dowolnym typem delegowanym z parametrami niezwiązanymi z ref / out, o ile typ zwracany jest zgodny. Zasadniczo mówisz „Nie dbam o parametry”. Zauważ, że delegate { ... }to nie to samo co delegate() { ... }- to ostatnie jest kompatybilne tylko z bezparametrowym typem delegata.
Jon Skeet
2

W dwóch powyższych przykładach nie ma różnicy, zero.

Ekspresja:

() => { x = 0 }

jest wyrażeniem Lambda z treścią instrukcji, więc nie można go skompilować jako drzewa wyrażeń. W rzeczywistości nawet się nie kompiluje, ponieważ potrzebuje średnika po 0:

() => { x = 0; } // Lambda statement body
() => x = 0      // Lambda expression body, could be an expression tree. 
Olmo
źródło
6
Z pewnością oznacza to różnicę: „jeden się skompiluje, a drugi nie”;)
Jon Skeet,
2

Amy B ma rację. Zauważ, że korzystanie z drzew wyrażeń może mieć zalety. LINQ to SQL zbada drzewo wyrażeń i skonwertuje je na SQL.

Możesz także grać w sztuczki z lamdami i drzewami ekspresji, aby skutecznie przekazywać nazwiska członków klasy do struktury w sposób bezpieczny dla refaktoryzacji. Moq jest tego przykładem.

Daniel Plaisted
źródło
-1

Jest różnica

Przykład:

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

I zamieniam na lambda: (błąd)

var mytask = Task.Factory.StartNew(() =>
{
    Thread.Sleep(5000);
    return 2712;
});
mytask.ContinueWith(()=>
{
    _backgroundTask.ContinueTask(() =>lblPercent.Content = mytask.Result.ToString(CultureInfo.InvariantCulture));
});
Án Bình Trọng
źródło
Niepoprawne ~ lambda nie powiodło się tylko dlatego, że sygnatura parametru metody nie pasuje.
Jack Wang
-1

Kilka podstawowych informacji tutaj.

To jest anonimowa metoda

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

Ponieważ anonimowe metody nie mają nazw, potrzebujemy delegata, w którym możemy przypisać obie te metody lub wyrażenia. na przykład

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

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

To samo z wyrażeniem lambda. Zwykle potrzebujemy delegata, aby z nich skorzystać

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

Możemy użyć delegata func, aby użyć tego wyrażenia.

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

bool result = checkStudentAge ( Student Object);
Yogesh Prajapati
źródło