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.

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ć.


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. 

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.


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

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);
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.