Am 14. September 2017 haben wir eine überarbeitete Fassung unserer Datenschutzrichtlinie veröffentlicht. Wenn Sie video2brain.com weiterhin nutzen, erklären Sie sich mit diesem überarbeiteten Dokument einverstanden. Bitte lesen Sie es deshalb sorgfältig durch.

Visual C# 2012 Grundkurs

Anonyme Delegates

LinkedIn Learning kostenlos und unverbindlich testen!

Jetzt testen Alle Abonnements anzeigen
Anonyme Methoden vereinfachen den Code, um Delegates zu instanziieren, weil keine separaten Methoden erstellt werden müssen. Die Lambda-Expression stellt eine anonyme Funktion dar, um Delegates zu erstellen.

Transkript

In diesem Abschnitt werde ich Ihnen den Umgang mit anonymen Delegates und Lambda-Expressions zeigen. Dabei werden wir einen ganz tiefen Blick unter die Haube werfen, weil es sehr interessant ist, was der C#-Compiler alles aus unserem Code produziert. Ich habe hier eine Liste mit Integer-Werten; diese Werte sind unsortiert. Ich sortiere die Werte hier, mit der Methode "Sort", der Klasse "List of Int". Und ich kann dieser Methode einen Delegate übergeben, der den Vergleich der Elemente übernimmt. In dem Fall habe ich zwei Implementierungen geschrieben, die zu diesem Delegate passen. Das wäre "Ascending" und "Descending". Das Problem, das mit dieser Schreibweise auftaucht, ist einfach, dass diese Methoden aus der Sicht herausrutschen können. Nun habe ich hier diesen Aufruf von "Sort". Man kann jetzt zwar relativ gut erraten, dass "Ascending" in einer bestimmten Reih- enfolge sortieren oder vergleichen soll, aber in etwas schwierigeren Szenarien ist nicht immer ersichtlich, was an dieser Stelle passieren soll. Es wäre eigentlich sehr schön, direkt beim Aufrufen von "Sort" sehen zu können, in welcher Weise die einzelnen Elemente verglichen werden. Dafür gibt es eine Lösung in .NET oder in C#, und diese Lösung heißt "Anonyme Delegates". Dafür benutze ich wiederum das Schlüsselwort "Delegate", aber diesmal innerhalb dieser Klammer. Ich sage: Ich habe hier einen Delegate, der nimmt einen Integer i1 und dann Integer i2 und führt irgendwelchen Code aus, der in geschweiften Klammern steht. In diesen Code kann ich jetzt meinen Vergleich reinschreiben. "return i1 i2", das wäre sozusagen der aufsteigende Vergleich. Das Wunderbare dabei ist, dass dieses Konstrukt von "Sort" genauso als Delegate akzeptiert wird, wie eine extra geschriebene Methode. Am Ende werden wir feststellen, dass es letztendlich auch gar nichts anderes ist, als eine Methode. Nur ist es so, dass diese Methode ja nur innerhalb dieser Klammern hier sichtbar ist, und dadurch auch gar keinen Namen braucht. Ein Name würde gar keinen Sinn machen. Und genau deswegen heißen die Dinger auch "anonyme Delegates". In dem Sinn kann ich mir diese ganzen Implementierungen hier schenken. D.h., mein Code wird dadurch wesentlich einfacher, auch besser lesbar. Und je nachdem, wie ich die Sortierung brauche, kann ich das direkt vor Ort, an der Stelle, wo ich die Liste sortiere, tun. Wir führen das aus, um zu sehen, ob es auch wirklich klappt. In dem Fall habe ich die umgekehrte Sortierung genommen, also dass die großen Werte zuerst erscheinen, und genauso steht es auch hier. Man kann also direkt in dem Kontext, in dem "Sort" aufgerufen wird, sehen, wie der Vergleich zwischen den Elementen stattfindet. Jetzt möchte ich Sie auf einen Ausflug  ausführen in "Microsoft Intermediate Language Code". Ich starte den Explorer und gehe in das Ausführungsverzeichnis von diesem Programm, das wir gerade kompiliert haben und benenne das Ganze um in eine DLL. Diese Umbenennung dient nur dem einfachen Öffnen der DLL mit dem Microsoft IL-Disassembler. Das könnte ich mit einer Exe über die rechte Maustaste nicht machen. Jetzt habe ich das Ding geöffnet; wir sehen jetzt in diesem Disassembler so einen Baum; da sehen wir z.B. unseren Delegate-Typ, der interessiert uns jetzt aber eigentlich nicht so sonderlich, sondern uns interessiert, was in dieser Klasse "Program" passiert. Hier interessiert mich jetzt: Was ist in der Methode DoIt los? Wir wissen ja, wir wollen eine Liste sortieren; das Sortieren einer Liste nimmt ja einen Delegate vom Typ "System.Comparison of Int" als Parameter an, und hier sehen wir  das schon. System.Comparison of Int32 in diesem Fall. Das ist die IL-Sprache, die hat ein bisschen andere Schreibweisen der Datentypen; aber im Grunde genommen ist es genau das Gleiche, wie in unserem Code so ein "System.Comparison of Int". Und diese Zeile sagt nichts anderes, als dass ein neues Objekt von diesem Delegate-Typ angelegt werden soll. Da interessiert uns jetzt natürlich: Was wird vorher als Parameter übergeben? Es ist also eine Zeile vorher, da sehen wir: Da wird eine Funktion geladen. "Load Function" heißt dieser Befehl und hier wird eine Funktion geladen, die heißt "DoIt b_ _0". Und jetzt ist natürlich die Frage: Wo kommt diese DoIt-Methode her? Aber im Grunde genommen wissen wir die Lösung schon: Es ist unser anonymer Delegate. Ich klappe das jetzt mal zu und jetzt sehen wir hier die Mitgliedselemente der Program-Klasse und hier können wir diese "DoIt b_ _0"-Methode sehen. Ich klappe die jetzt mal auf und diese Methode ist eigentlich, obwohl wahrscheinlich die meisten den IL-Code gar nicht kennen werden. Dennoch ist diese Methode relativ leicht zu verstehen. Also man lädt das Argument 1, man lädt das Argument 0, und subtrahiert die beiden voneinander. Diese drei Zeilen eliminieren sich sozusagen gegenseitig, die kann man im Grunde genommen vernachlässigen. Und so kann man sagen, dass das Ergebnis dieser Subtraktion als Return-Wert am Ende der Methode zurückgegeben wird. Das war relativ einfach zu verstehen. Aber der Witz an der Geschichte ist eigentlich der, dass ich hiermit zeigen kann, dass aus einem anonymen Delegate letztendlich auch nichts anderes als eine Methode entsteht, so, als ob wir die Methode sonst irgendwohin im Code geschrieben und den Namen der Methode übergeben hätten. Nur, dass wir jetzt im Code den Vorteil der besseren Lesbarkeit haben. Um die Dinge jetzt noch ein klein wenig verwirrender zu machen, ist es so, dass seit .NET 3.5 Microsoft noch einmal neue Konstrukte in die Sprache C# und überhaupt in das darunter liegende .NET Framework eingebracht hat. Und diese neuen Konstrukte nennen sich "Lambda Expressions". Ich kann jetzt nicht näher auf diese Lambda Expressions eingehen; ich kann nur sagen, dass die Lambda Expressions ein sehr, sehr leistungsfähiges Konstrukt sind. Dieses Konstrukt kann viel, viel mehr als nur anonyme Delegates erzeugen. Aber unter anderem kann es eben auch anonyme Delegates beschreiben und das mit einer Schreibweise, die noch eleganter ist als das, was wir hier sehen. Und zwar ist es so, dass der Compiler an der Stelle, wo ich diesen anonymen Delegate hier definiere, eigentlich von selber weiß, dass die beiden Parameter, die ich hier übergebe, vom Typ Integer sein müssten. Also kann ich im Grunde genommen das Ganze so schreiben. Und jetzt lösche ich auch noch das Wort Delegate weg, weil das ist eigentlich auch klar, und jetzt kann ich sagen, diese beiden Symbole i1 und i2 werden projiziert auf diesen Code. Und dieser Operator, den wir da sehen, dieses = dieser Operator wird im Englischen "Goes to" gesprochen, also "geht zu"; "geht über in", würde man es vielleicht ins Deutsche übersetzen. Also die beiden Symbole i1 und i2 gehen über in einen Integer-Wert, der i2 i1 entspricht. Wie man sieht, meckert der Compiler jetzt überhaupt nichts an, der nimmt diese Lambda Expression genauso an, wie einen Delegate oder einen anonymen Delegate. Dann werde ich das Ganze kompilieren, und dann gehen wir noch einmal in das Ausführungsverzeichnis und schauen uns nochmals den IL-Code an, weil jetzt natürlich interessant ist, ob sich da irgend etwas verändert hat. Das mache ich jetzt. "Open Folder in Windows Explorer", in "Debug" jetzt muss ich die vorhin umbenannte DLL löschen und benenne diese Exe, die ich frisch erzeugt habe, in DLL um, und kann mit "Öffnen mit" den IL-Disassembler wieder starten. Jetzt gehe ich wieder in diese Program-Klasse rein, und man erkennt schon, hier gibt es exakt die gleiche Methode: "DoIt E _ _ 0". Wenn ich die öffne, ist da genau der gleiche Code drinnen. Das würde uns auch sehr verwundern, wenn etwas Anderes drin stünde, denn den Code selber habe ich eigentlich gar nicht verändert. Jetzt wäre also die Frage, ob diese Lambda Expression irgendetwas im aufrufenden Kontext geändert hat. Und deswegen rufe ich jetzt noch einmal diese Methode DoIt auf. Und hier sehen wir wieder an der gleichen gewohnten Stelle wie vorhin dieses "New Object", vom Typ System.Comparison of Int32. Und in der Zeile davor dieses "Load Function", und da sieht man diese "DoIt E_ _0" Methode als Funktionszeiger geladen. Fazit des Ganzen ist: Wir können statt extra geschriebener Methoden deren Namen, oder deren Symbol wir dann hier an die Sort-Methode übergeben, gerade so gut anonyme Delegates schreiben, oder Lambda Expressions. Und alle drei Methoden, wie man einen Parameter für die Methode Sort erzeugt, kommen letztendlich auf exakt das gleiche Ergebnis heraus. Aber es gibt eine kleine Einschränkung. Nämlich, im Augenblick ist es so, dass dieser anonyme Delegate letztendlich für sich steht. Er arbeitet mit zwei Symbolen - i1 und i2. Und außer diesen Symbolen wird in diesem Kontext des anonymen Delegates nichts anderes verwendet. Aber es geht noch besser. Angenommen, diese Methode hätte jetzt irgendeine Variable, "int var" nenne ich sie. Wir initialisieren sie mit irgendeinem Wert. Jetzt gehe ich her und ändere diese Methode so ab, dass ich diese Variable "var" dazu addiere. Und jetzt zeigt sich auf der einen Seite einer der großen Vorteile der anonymen Delegates, bzw. der Lambda Expressions gegenüber Extra-Methoden. Weil, hätte ich irgendwo da unten eine Methode geschrieben, hätte diese Methode gar keinen Zugriff auf diese Variable var gehabt. Aber so ein anonymer Delegate, oder eine Lambda Expression, hat nun Zugriff auf diese Variable, kann sich somit Informationen aus dem Kontext, aus dem umgebenden Kontext, in die Methode hinein ziehen. Das ist jetzt natürlich schon sehr, sehr elegant. Aber da kommt ein ganz, ganz großes "Cave canem". Ich klappe jetzt nämlich noch einmal den IL-Code der ganzen Geschichte auf. Ich lösche die bereits vorhin erzeugte DLL, nehme unsere frisch erzeugte exe-Datei, benenne sie in DLL um, öffne sie jetzt mit dem IL-Disassembler, gehe jetzt wieder in dieses Program rein. Und jetzt sieht man, hier hat sich etwas verändert. Diese "DoIt b_ _0" Methode existiert nämlich nicht mehr. Jetzt gehen wir doch mal rein in diesen Kontext. Hier sehen wir wieder den Aufruf von System.Comparison. Also im Grunde genommen muss ja in der Zeile davor dann dieser Funktions-Pointer geladen werden. Und jetzt sehen wir: Es gibt hier zwar eine Methode DoIt b_ _0, aber diese Methode steht jetzt nicht mehr als ein Element des Datentyps "Program" da, sondern dieser Slash heißt, ich habe hier einen verschachtelten Datentyp, der innerhalb von "Program" definiert ist. Und dieser Datentyp hat den etwas unaussprechlichen Namen mit diesen spitzen Klammern und "c_ _display class one". Jetzt interessiert uns natürlich: Was ist denn diese Display Class One? Wo kommt die her und wo steht sie? Hier steht sie; sie ist also, wie gesagt, geschachtelt innerhalb vom Program. Diese Display Class One hat jetzt ihrerseits mehrere Elemente. Unter anderem erkennen wir diese Methode DoIt; wenn ich die jetzt aufklappe, dann sehen wir, dass es sich mehr oder minder um dieselbe Methode wie vorhin handelt; dass wir also die Argumente 2 und 1 voneinander subtrahieren. Aber jetzt kommt noch dieses Feld var dazu, das ich ja eben dazu addiere. Das war ja die Erweiterung des Codes. Und jetzt sieht man, dass diese Variable var sich auch nicht mehr im Program befindet, sondern die hat der Compiler automatisch in diese Display Class One hinein verschoben. Die ist also gar kein Mitglied mehr von dieser Klasse Program, sondern sie ist Mitglied dieser Display Class One. Hier sieht man es: es ist eine öffentliche Variable, damit sie nämlich vom aufrufenden Kontext, also von Program aus, auch wirklich gesetzt werden kann. Und damit können wir unter dem Strich sagen: Wenn wir anonyme Delegates oder Lambda Expressions verwenden, dann können wir auf der einen Seite zwar Variablen oder sonstige Informationen aus dem aufrufenden Kontext verwenden. Auf der anderen Seite verändert sich dadurch der Code; der Code, den der Compiler dafür generiert. Es wird eine neue Klasse gemacht; eine Variable verschiebt sich auf einmal von unserer eigentlichen Klasse in diese neue Klasse. Das Problem, das damit auftaucht ist, dass wenn jemand sich den Code anschaut, dann meint er, diese Variable var wäre eine Variable der Klasse Program. Das ist aber nicht der Fall und hier können potenzielle Probleme bei der parallelen Ausführung von Programmen entstehen. Diese potenziellen Probleme möchte ich an dieser Stelle noch nicht erklären, weil es dazu einfach noch etwas mehr Hintergrundwissen zur parallelen Ausführung von Programmen braucht. Wir haben in diesem Abschnitt gelernt, wie wir die Schreibweise von Delegates wesentlich vereinfachen können. Es gibt dafür zwei äquivalente Methoden. Das sind anonyme Delegates und Lambda Expressions. Mit dieser Vereinfachung ergibt sich auch die Möglichkeit, Variablen aus dem Kontext mit in den anonymen Delegate zu übernehmen; aber daraus können unter Umständen Probleme resultieren.

Visual C# 2012 Grundkurs

Schreiben Sie eigene Programme in C# und lernen Sie dazu alle Schlüsselwörter und die meisten Konstrukte kennen, um sicher mit dieser Programmierspreche umzugehen.

7 Std. 1 min (44 Videos)
Derzeit sind keine Feedbacks vorhanden...
 

Dieser Online-Kurs ist als Download und als Streaming-Video verfügbar. Die gute Nachricht: Sie müssen sich nicht entscheiden - sobald Sie das Training erwerben, erhalten Sie Zugang zu beiden Optionen!

Der Download ermöglicht Ihnen die Offline-Nutzung des Trainings und bietet die Vorteile einer benutzerfreundlichen Abspielumgebung. Wenn Sie an verschiedenen Computern arbeiten, oder nicht den ganzen Kurs auf einmal herunterladen möchten, loggen Sie sich auf dieser Seite ein, um alle Videos des Trainings als Streaming-Video anzusehen.

Wir hoffen, dass Sie viel Freude und Erfolg mit diesem Video-Training haben werden. Falls Sie irgendwelche Fragen haben, zögern Sie nicht uns zu kontaktieren!