Schlüsselwort gegen Lambda-Notation delegieren

Antworten:

140

Kurze Antwort: nein.

Längere Antwort, die möglicherweise nicht relevant ist:

  • Wenn Sie das Lambda einem Delegatentyp (z. B. Funcoder Action) zuweisen, erhalten Sie einen anonymen Delegaten.
  • Wenn Sie das Lambda einem Ausdruckstyp zuweisen, erhalten Sie anstelle eines anonymen Delegaten einen Ausdrucksbaum. Der Ausdrucksbaum kann dann zu einem anonymen Delegaten kompiliert werden.

Bearbeiten: Hier sind einige Links für Ausdrücke.

  • System.Linq.Expression.Expression (TDelegate) (hier beginnen).
  • Linq In-Memory mit Delegaten (wie System.Func) verwendet System.Linq.Enumerable . Linq to SQL (und alles andere) mit Ausdrücken verwendet System.Linq.Queryable . Überprüfen Sie die Parameter dieser Methoden.
  • Eine Erklärung von ScottGu . Kurz gesagt, Linq In-Memory erstellt einige anonyme Methoden zur Lösung Ihrer Anfrage. Linq to SQL erstellt einen Ausdrucksbaum, der die Abfrage darstellt, und übersetzt diesen Baum dann in T-SQL. Linq to Entities erstellt einen Ausdrucksbaum, der die Abfrage darstellt, und übersetzt diesen Baum dann in plattformgerechtes SQL.
Amy B.
quelle
3
Ein Ausdruckstyp? Das klingt für mich nach Neuland. Wo kann ich mehr über Ausdruckstypen und die Verwendung von Ausdrucksbäumen in C # erfahren?
MojoFilter
2
Noch längere Antwort - es gibt fummelige Gründe, warum sie auch in verschiedene Delegiertypen konvertierbar sind :)
Jon Skeet
Beachten Sie, dass das Lambda nur dann einem Ausdruckstyp zugewiesen werden kann, wenn es sich um einen Ausdruck Lambda handelt.
Micha Wiedenmann
125

Ich mag Amys Antwort, aber ich dachte, ich wäre pedantisch. Die Frage sagt : „Sobald es kompiliert“ - was darauf schließen lässt , dass beide Ausdrücke haben zusammengestellt. Wie könnten sie beide kompilieren, wobei einer in einen Delegaten und einer in einen Ausdrucksbaum konvertiert wird? Es ist schwierig - Sie müssen eine andere Funktion anonymer Methoden verwenden. die einzige, die nicht von Lambda-Ausdrücken geteilt wird. Wenn Sie eine anonyme Methode angeben , ohne eine Parameterliste Angabe überhaupt ist es kompatibel mit jedem Delegattyp void zurückgibt und ohne outParameter. Mit diesem Wissen sollten wir in der Lage sein, zwei Überladungen zu konstruieren, um die Ausdrücke völlig eindeutig, aber sehr unterschiedlich zu machen.

Aber die Katastrophe schlägt zu! Zumindest mit C # 3.0 können Sie einen Lambda-Ausdruck mit einem Blockkörper nicht in einen Ausdruck konvertieren - noch können Sie einen Lambda-Ausdruck mit einer Zuweisung im Körper konvertieren (selbst wenn er als Rückgabewert verwendet wird). Dies kann sich mit C # 4.0 und .NET 4.0 ändern, wodurch mehr in einem Ausdrucksbaum ausgedrückt werden können. Mit anderen Worten, mit den Beispielen, die MojoFilter zufällig gegeben hat, werden die beiden fast immer in dasselbe konvertiert. (Weitere Details in einer Minute.)

Wir können den Trick der Delegate-Parameter verwenden, wenn wir die Körper ein wenig ändern:

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

Aber warte! Wir können zwischen den beiden unterscheiden, auch ohne Ausdrucksbäume zu verwenden, wenn wir schlau genug sind. Im folgenden Beispiel werden die Regeln für die Überlastungsauflösung (und der Trick zum Anpassen anonymer Delegaten) verwendet ...

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

Autsch. Denken Sie daran, Kinder, jedes Mal, wenn Sie eine von einer Basisklasse geerbte Methode überladen, fängt ein kleines Kätzchen an zu weinen.

Jon Skeet
quelle
9
Ich holte mein Popcorn heraus und las die ganze Sache. Das ist eine Unterscheidung, an die ich wahrscheinlich nie denken würde, selbst wenn ich sie direkt ins Gesicht starren würde.
MojoFilter
27
Ich wusste etwas davon, aber ich muss Ihnen zu Ihrer Fähigkeit gratulieren, es den Menschen mitzuteilen.
Amy B.
1
Für alle, die an den Änderungen in .NET 4.0 (basierend auf dem CTP) interessiert sind, siehe marcgravell.blogspot.com/2008/11/future-expressions.html . Beachten Sie, dass C # 4.0, soweit ich das beurteilen kann, noch nichts Neues macht.
Marc Gravell
4
Jon, du rockst. Erik, um ein echter Skeet-Fanboi zu sein, solltest du seinen Stack-Überlauf-RSS wie ich abonnieren. Stecken Sie einfach stackoverflow.com/users/22656 in Ihren Feedreader .
Paul Batum
3
@RoyiNamir: Wenn Sie eine anonyme Methode ohne Parameterliste verwenden, ist diese mit jedem Delegattyp mit Nicht-Ref / Out-Parametern kompatibel, sofern der Rückgabetyp kompatibel ist. Grundsätzlich sagst du "Die Parameter sind mir egal". Beachten Sie, dass delegate { ... }ist nicht das gleiche wie delegate() { ... }- letztere nur kompatibel mit einer parameterlos Delegattyp ist.
Jon Skeet
2

In den beiden obigen Beispielen gibt es keinen Unterschied, Null.

Der Ausdruck:

() => { x = 0 }

ist ein Lambda-Ausdruck mit Anweisungshauptteil und kann daher nicht als Ausdrucksbaum kompiliert werden. Tatsächlich wird es nicht einmal kompiliert, da es nach 0 ein Semikolon benötigt:

() => { x = 0; } // Lambda statement body
() => x = 0      // Lambda expression body, could be an expression tree. 
Olmo
quelle
6
Das bedeutet sicherlich, dass es einen Unterschied gibt: "Einer wird kompilieren, der andere nicht";)
Jon Skeet
2

Amy B ist richtig. Beachten Sie, dass die Verwendung von Ausdrucksbäumen Vorteile haben kann. LINQ to SQL untersucht den Ausdrucksbaum und konvertiert ihn in SQL.

Sie können auch Streiche mit Lamdas und Ausdrucksbäumen spielen, um die Namen von Klassenmitgliedern auf refactoring-sichere Weise effektiv an ein Framework zu übergeben. Moq ist ein Beispiel dafür.

Daniel Plaisted
quelle
-1

Da ist ein Unterschied

Beispiel:

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

Und ich ersetze durch Lambda: (Fehler)

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
quelle
Falsches ~ Lambda ist fehlgeschlagen, nur weil die Signatur der Methodenparameter nicht übereinstimmt.
Jack Wang
-1

Einige Grundlagen hier.

Dies ist eine anonyme Methode

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

Da anonyme Methoden keine Namen haben, benötigen wir einen Delegaten, in dem wir beide Methoden oder Ausdrücke zuweisen können. z.B

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

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

Gleiches gilt für den Lambda-Ausdruck. Normalerweise brauchen wir einen Delegierten, um sie zu benutzen

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

Wir können einen func-Delegaten verwenden, um diesen Ausdruck zu verwenden.

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

bool result = checkStudentAge ( Student Object);
Yogesh Prajapati
quelle