parola chiave delegata vs. notazione lambda


Risposte:


140

Risposta breve: no.

Risposta più lunga che potrebbe non essere pertinente:

  • Se assegni lambda a un tipo delegato (come Funco Action) otterrai un delegato anonimo.
  • Se assegni lambda a un tipo di espressione, otterrai un albero delle espressioni anziché un delegato anonimo. L'albero delle espressioni può quindi essere compilato per un delegato anonimo.

Modifica: ecco alcuni link per le espressioni.

  • System.Linq.Expression.Expression (TDelegate) (inizia qui).
  • Linq in memoria con delegati (come System.Func) utilizza System.Linq.Enumerable . Linq to SQL (e qualsiasi altra cosa) con espressioni utilizza System.Linq.Queryable . Controlla i parametri su questi metodi.
  • Una spiegazione di ScottGu . In poche parole, Linq in memoria produrrà alcuni metodi anonimi per risolvere la tua query. Linq to SQL produrrà un albero delle espressioni che rappresenta la query e quindi tradurrà tale albero in T-SQL. Linq to Entities produrrà un albero delle espressioni che rappresenta la query e quindi tradurrà tale albero in SQL appropriato per la piattaforma.

3
Un tipo di espressione? Mi sembra un nuovo territorio. Dove posso trovare ulteriori informazioni sui tipi di espressioni e sull'utilizzo degli alberi delle espressioni in C #?
MojoFilter,

2
Risposta ancora più lunga: ci sono anche molte ragioni per cui sono convertibili in diversi tipi di delegati :)
Jon Skeet,

Si noti che il lambda può essere assegnato solo a un tipo di espressione, se si tratta di un'espressione lambda.
Micha Wiedenmann l'

125

Mi piace la risposta di Amy, ma ho pensato di essere pedante. La domanda dice "Una volta compilata", il che suggerisce che entrambe le espressioni sono state compilate. Come hanno potuto compilare entrambi, ma con uno convertito in un delegato e uno in un albero delle espressioni? È complicato: devi usare un'altra funzione di metodi anonimi; l'unico che non è condiviso dalle espressioni lambda. Se si specifica un metodo anonimo senza specificare un elenco di parametri a tutti che è compatibile con qualsiasi tipo delegato tornare vuoto e senza outparametri. Forti di questa conoscenza, dovremmo essere in grado di costruire due sovraccarichi per rendere le espressioni completamente inequivocabili ma molto diverse.

Ma il disastro colpisce! Almeno con C # 3.0, non è possibile convertire un'espressione lambda con un corpo di blocco in un'espressione, né è possibile convertire un'espressione lambda con un'assegnazione nel corpo (anche se viene utilizzata come valore di ritorno). Questo può cambiare con C # 4.0 e .NET 4.0, che consentono di esprimere di più in un albero delle espressioni. Quindi, in altre parole, con gli esempi forniti da MojoFilter, i due saranno quasi sempre convertiti nella stessa cosa. (Maggiori dettagli in un minuto.)

Possiamo usare il trucco dei parametri delegati se cambiamo un po 'i corpi però:

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

Ma aspetta! Possiamo distinguere tra i due anche senza usare alberi delle espressioni, se siamo abbastanza furbi. L'esempio seguente utilizza le regole di risoluzione del sovraccarico (e il trucco di corrispondenza del delegato anonimo) ...

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

Ahia. Ricorda i bambini, ogni volta che sovraccarichi un metodo ereditato da una classe base, un gattino inizia a piangere.


9
Ho tirato fuori i popcorn e ho letto tutto. Questa è una distinzione a cui probabilmente non avrei mai pensato, anche se lo stessi fissando in faccia.
MojoFilter,

27
Ne sapevo un po ', ma devo congratularmi con te per la tua capacità di comunicarlo agli umani.
Amy B,

1
Per chiunque sia interessato alle modifiche a .NET 4.0 (basato sul CTP), consultare marcgravell.blogspot.com/2008/11/future-expressions.html . Si noti che C # 4.0 non fa ancora nulla di nuovo per quanto posso dire.
Marc Gravell

4
Jon, sei un rock. Erik, per essere un vero fanboi di Skeet, dovresti essere iscritto al suo stack overflow rss come me. Basta inserire stackoverflow.com/users/22656 nel lettore di feed.
Paul Batum,

3
@RoyiNamir: se si utilizza un metodo anonimo senza un elenco di parametri, è compatibile con qualsiasi tipo di delegato con parametri non ref / out, purché il tipo restituito sia compatibile. Fondamentalmente stai dicendo "Non mi interessano i parametri". Nota che nondelegate { ... } è lo stesso di - quest'ultimo è compatibile solo con un tipo delegato senza parametri. delegate() { ... }
Jon Skeet,

2

Nei due esempi sopra non c'è differenza, zero.

L'espressione:

() => { x = 0 }

è un'espressione Lambda con corpo dell'istruzione, quindi non può essere compilata come un albero delle espressioni. In realtà non si compila nemmeno perché ha bisogno di un punto e virgola dopo 0:

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

6
Sicuramente ciò significa che c'è una differenza di "uno si compila, l'altro no";)
Jon Skeet,

2

Amy B ha ragione. Si noti che possono esserci vantaggi nell'uso degli alberi delle espressioni. LINQ to SQL esaminerà l'albero delle espressioni e lo convertirà in SQL.

Puoi anche giocare brutti scherzi con i lama e gli alberi delle espressioni per passare efficacemente i nomi dei membri della classe a un framework in modo sicuro dal refactoring. Moq ne è un esempio.


-1

C'è una differenza

Esempio:

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

E sostituisco con lambda: (errore)

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

Wrong ~ lambda fallito solo perché la firma del parametro del metodo non corrisponde.
Jack Wang

-1

Alcune basi qui.

Questo è un metodo anonimo

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

Poiché i metodi anonimi non hanno nomi, abbiamo bisogno di un delegato in cui possiamo assegnare entrambi questi metodi o espressioni. per esempio

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

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

Lo stesso con l'espressione lambda. Di solito abbiamo bisogno di un delegato per usarli

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

Possiamo usare un delegato di funzione per usare questa espressione.

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

bool result = checkStudentAge ( Student Object);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.