Was ist eine Schließung ? Haben wir sie in .NET?
Wenn sie in .NET vorhanden sind, können Sie bitte ein Code-Snippet (vorzugsweise in C #) bereitstellen, das dies erklärt.
Ich habe einen Artikel zu diesem Thema . (Es gibt viele Beispiele.)
Im Wesentlichen ist ein Abschluss ein Codeblock, der zu einem späteren Zeitpunkt ausgeführt werden kann, aber die Umgebung beibehält, in der er zuerst erstellt wurde - dh er kann auch danach noch die lokalen Variablen usw. der Methode verwenden, die ihn erstellt hat Die Ausführung der Methode ist beendet.
Die allgemeine Funktion von Closures wird in C # durch anonyme Methoden und Lambda-Ausdrücke implementiert.
Hier ist ein Beispiel mit einer anonymen Methode:
using System;
class Test
{
static void Main()
{
Action action = CreateAction();
action();
action();
}
static Action CreateAction()
{
int counter = 0;
return delegate
{
// Yes, it could be done in one statement;
// but it is clearer like this.
counter++;
Console.WriteLine("counter={0}", counter);
};
}
}
Ausgabe:
counter=1
counter=2
Hier können wir sehen, dass die von CreateAction zurückgegebene Aktion weiterhin Zugriff auf die Zählervariable hat und diese tatsächlich erhöhen kann, obwohl CreateAction selbst abgeschlossen ist.
counter
kann inkrementiert werden. Der Compiler generiert eine Klasse, die eincounter
Feld enthält , und jeder Code, auf dencounter
verwiesen wird, durchläuft eine Instanz dieser Klasse.Wenn Sie sehen möchten, wie C # Closure implementiert, lesen Sie "Ich kenne die Antwort (sein 42) Blog".
Der Compiler generiert im Hintergrund eine Klasse, um die anoymöse Methode und die Variable j zu kapseln
für die Funktion:
Verwandeln Sie es in:
quelle
Closures sind Funktionswerte, die variable Werte aus ihrem ursprünglichen Bereich beibehalten. C # kann sie in Form von anonymen Delegierten verwenden.
Nehmen Sie für ein sehr einfaches Beispiel diesen C # -Code:
Am Ende wird die Leiste auf 4 gesetzt, und der myClosure-Delegat kann weitergegeben werden, um an anderer Stelle im Programm verwendet zu werden.
Closures können für viele nützliche Dinge verwendet werden, z. B. für die verzögerte Ausführung oder zur Vereinfachung von Schnittstellen. LINQ wird hauptsächlich mit Closures erstellt. Für die meisten Entwickler ist es am unmittelbarsten, Ereignisbehandlungsroutinen zu dynamisch erstellten Steuerelementen hinzuzufügen. Sie können Abschlüsse verwenden, um Verhalten hinzuzufügen, wenn das Steuerelement instanziiert wird, anstatt Daten an anderer Stelle zu speichern.
quelle
Ein Abschluss ist eine anonyme Funktion, die außerhalb der Funktion übergeben wird, in der er erstellt wurde. Es verwaltet alle Variablen aus der Funktion, in der es erstellt wird, die es verwendet.
quelle
Hier ist ein erfundenes Beispiel für C #, das ich aus ähnlichem Code in JavaScript erstellt habe:
Hier ist ein Code, der zeigt, wie der obige Code verwendet wird ...
Hoffe das ist etwas hilfreich.
quelle
Grundsätzlich ist Closure ein Codeblock, den Sie als Argument an eine Funktion übergeben können. C # unterstützt Schließungen in Form von anonymen Delegierten.
Hier ist ein einfaches Beispiel: Die
List.Find-Methode kann Code akzeptieren und ausführen (Abschluss), um das Element der Liste zu finden.
Mit der C # 3.0-Syntax können wir Folgendes schreiben:
quelle
Ein Abschluss liegt vor, wenn eine Funktion in einer anderen Funktion (oder Methode) definiert ist und die Variablen der übergeordneten Methode verwendet . Diese Verwendung von Variablen, die sich in einer Methode befinden und in eine darin definierte Funktion eingeschlossen sind, wird als Abschluss bezeichnet.
Mark Seemann hat einige interessante Beispiele für Schließungen in seinem Blogbeitrag , wo er hat eine paralel zwischen oop und funktionalen Programmierung.
Und um es detaillierter zu machen
quelle
Closures sind Codestücke, die auf eine Variable außerhalb von sich selbst verweisen (von unten auf dem Stapel), die möglicherweise später aufgerufen oder ausgeführt werden (z. B. wenn ein Ereignis oder ein Delegat definiert ist und zu einem unbestimmten zukünftigen Zeitpunkt aufgerufen werden kann) ) ... Da die externe Variable, auf die der Codeblock verweist, möglicherweise nicht mehr gültig ist (und sonst verloren gegangen wäre), weist die Tatsache, dass der Codeblock (als Closure bezeichnet) auf ihn verweist, die Laufzeit an, "zu halten" "Diese Variable im Gültigkeitsbereich, bis sie vom Abschlusscode-Code nicht mehr benötigt wird ...
quelle
Ich habe versucht, es auch zu verstehen. Weiter unten finden Sie die Codefragmente für Same Code in Javascript und C #, die den Abschluss anzeigen.
JavaScript:
C #:
JavaScript:
C #:
quelle
Aus heiterem Himmel eine einfache und verständnisvollere Antwort aus dem Buch C # 7.0 auf den Punkt gebracht.
Voraussetzung, die Sie kennen sollten : Ein Lambda-Ausdruck kann auf die lokalen Variablen und Parameter der Methode verweisen, in der er definiert ist (äußere Variablen).
Realteil : Äußere Variablen, auf die ein Lambda-Ausdruck verweist, werden als erfasste Variablen bezeichnet. Ein Lambda-Ausdruck, der Variablen erfasst, wird als Abschluss bezeichnet.
Letzter zu beachtender Punkt : Erfasste Variablen werden ausgewertet, wenn der Delegat tatsächlich aufgerufen wird, nicht, wenn die Variablen erfasst wurden:
quelle
Wenn Sie eine anonyme Inline-Methode (C # 2) oder (vorzugsweise) einen Lambda-Ausdruck (C # 3 +) schreiben, wird noch eine tatsächliche Methode erstellt. Wenn dieser Code eine lokale Variable im äußeren Bereich verwendet, müssen Sie diese Variable trotzdem irgendwie an die Methode übergeben.
Nehmen Sie z. B. diese Linq Where-Klausel (eine einfache Erweiterungsmethode, die einen Lambda-Ausdruck übergibt):
Wenn Sie i in diesem Lambda-Ausdruck verwenden möchten, müssen Sie es an diese erstellte Methode übergeben.
Die erste Frage, die sich stellt, lautet: Soll sie als Wert oder Referenz übergeben werden?
Referenzübergabe ist (ich denke) vorzuziehen, da Sie Lese- / Schreibzugriff auf diese Variable erhalten (und dies ist, was C # tut; ich denke, das Team in Microsoft hat die Vor- und Nachteile abgewogen und sich für Referenzangaben entschieden; laut Jon Skeet Artikel , Java ging mit By-Value).
Aber dann stellt sich eine andere Frage: Wo soll ich das zuordnen?
Sollte es tatsächlich / natürlich auf dem Stapel zugeordnet werden? Wenn Sie es dem Stapel zuweisen und als Referenz übergeben, kann es Situationen geben, in denen es seinen eigenen Stapelrahmen überlebt. Nehmen Sie dieses Beispiel:
Der Lambda-Ausdruck (in der Where-Klausel) erstellt erneut eine Methode, die auf ein i verweist. Wenn i auf dem Stapel von Outlive zugewiesen ist, zeigt das in der generierten Methode verwendete i zum Zeitpunkt der Aufzählung der whereItems auf das i von Outlive, dh auf eine Stelle im Stapel, auf die nicht mehr zugegriffen werden kann.
Ok, dann brauchen wir es auf dem Haufen.
Der C # -Compiler unterstützt dieses Inline-Anonym / Lambda mit der Bezeichnung " Closures ": Er erstellt auf dem Heap eine Klasse namens ( ziemlich schlecht ) DisplayClass, die ein Feld enthält, das das i und die tatsächlich verwendete Funktion enthält es.
Etwas, das dem äquivalent wäre (Sie können die mit ILSpy oder ILDASM generierte IL sehen):
Es instanziiert diese Klasse in Ihrem lokalen Bereich und ersetzt jeden Code, der sich auf i oder den Lambda-Ausdruck bezieht, durch diese Abschlussinstanz. Wenn Sie also das i in Ihrem "lokalen Bereich" -Code verwenden, in dem ich definiert wurde, verwenden Sie tatsächlich dieses DisplayClass-Instanzfeld.
Wenn ich also das "lokale" i in der Hauptmethode ändern würde, würde es tatsächlich _DisplayClass.i ändern;
dh
es wird 12 ausgedruckt, wenn "i = 10" zu diesem Disclalyclass-Feld geht und es kurz vor der 2. Aufzählung ändert.
Eine gute Quelle zu diesem Thema ist dieses Bart De Smet Pluralsight-Modul (Registrierung erforderlich) (ignorieren Sie auch seine fehlerhafte Verwendung des Begriffs "Heben" - was (ich denke) bedeutet, dass die lokale Variable (dh i) geändert wird, um darauf zu verweisen in das neue DisplayClass-Feld).
In anderen Nachrichten scheint es ein Missverständnis zu geben, dass "Closures" mit Schleifen zusammenhängen - wie ich verstehe, sind "Closures" KEIN Konzept, das sich auf Schleifen bezieht , sondern auf anonyme Methoden / Lambda-Ausdrücke, die Variablen mit lokalem Gültigkeitsbereich verwenden - obwohl dies ein Trick ist Fragen verwenden Schleifen, um dies zu demonstrieren.
quelle
Ein Abschluss ist eine Funktion, die innerhalb einer Funktion definiert ist und auf die lokalen Variablen sowie auf deren übergeordnete Funktion zugreifen kann.
also die Funktion innerhalb der find-Methode.
kann auf die Variablen in seinem Bereich t und auf den Variablennamen zugreifen, der sich in seinem übergeordneten Bereich befindet. Obwohl es von der find-Methode als Delegat ausgeführt wird, aus einem anderen Bereich insgesamt.
quelle