Freigeben über


System.Delegate und das delegate Schlüsselwort

Vorhergehend

In diesem Artikel werden die Klassen in .NET behandelt, die Stellvertretungen unterstützen und wie diese dem delegate Schlüsselwort zugeordnet werden.

Delegattypen definieren

Beginnen wir mit dem Schlüsselwort "Delegate", da dies in erster Linie das ist, was Sie beim Arbeiten mit Stellvertretungen verwenden werden. Der Code, den der Compiler generiert, wenn Sie das delegate Schlüsselwort verwenden, wird Methodenaufrufen zugeordnet, die Member der Klassen Delegate und MulticastDelegate aufrufen.

Sie definieren einen Delegattyp mithilfe einer Syntax, die dem Definieren einer Methodensignatur ähnelt. Sie fügen einfach das delegate Schlüsselwort zur Definition hinzu.

Wir verwenden nun weiterhin die List.Sort()-Methode als Beispiel. Der erste Schritt besteht darin, einen Typ für den Vergleichsdelegat zu erstellen:

// From the .NET Core library

// Define the delegate type:
public delegate int Comparison<in T>(T left, T right);

Der Compiler generiert eine Klasse, die von System.Delegate abgeleitet ist und der verwendeten Signatur entspricht (in diesem Fall eine Methode, die eine ganze Zahl zurückgibt und zwei Argumente hat). Der Typ dieses Delegaten ist Comparison. Der Delegattyp Comparison ist ein generischer Typ. Weitere Informationen finden Sie unter generische Klassen und Methoden.

Beachten Sie, dass die Syntax so aussehen kann, als würde sie eine Variable deklarieren, aber tatsächlich einen Typ deklarieren. Sie können Delegatentypen innerhalb von Klassen, direkt innerhalb von Namespaces oder sogar im globalen Namespace definieren.

Hinweis

Das Deklarieren von Delegattypen (oder anderen Typen) direkt im globalen Namespace wird nicht empfohlen.

Der Compiler generiert außerdem Add- und Remove-Handler für diesen neuen Typ, sodass Clients dieser Klasse Methoden aus der Aufrufliste einer Instanz hinzufügen und entfernen können. Der Compiler erzwingt, dass die Signatur der hinzugefügten oder entfernten Methode mit der Signatur übereinstimmt, die beim Deklarieren der Methode verwendet wird.

Deklarieren von Instanzen von Stellvertretungen

Nach dem Definieren des Delegaten können Sie eine Instanz dieses Typs erstellen. Wie alle Variablen in C# können Sie Stellvertretungsinstanzen nicht direkt in einem Namespace oder im globalen Namespace deklarieren.

// inside a class definition:

// Declare an instance of that type:
public Comparison<T> comparator;

Der Typ der Variablen ist Comparison<T>der zuvor definierte Delegattyp. Der Name der Variablen lautet comparator.

Dieser Codeausschnitt oben deklarierte eine Membervariable innerhalb einer Klasse. Sie können Stellvertretungsvariablen auch deklarieren, die lokale Variablen oder Argumente für Methoden sind.

Delegaten aufrufen

Sie rufen die Methoden auf, die sich in der Aufrufliste eines Delegaten befinden, indem Sie diesen Delegaten aufrufen. Innerhalb der Sort() Methode ruft der Code die Vergleichsmethode auf, um zu bestimmen, in welcher Reihenfolge Objekte platziert werden sollen:

int result = comparator(left, right);

In der obigen Zeile ruft der Code die an den Delegaten angefügte Methode auf . Sie behandeln die Variable als Methodennamen und rufen sie mithilfe der normalen Methodenaufrufssyntax auf.

Diese Codezeile beruht auf einer unsicheren Annahme: Es gibt keine Garantie, dass dem Delegierten ein Ziel hinzugefügt wurde. Wenn keine Ziele angefügt wurden, würde die obige Zeile NullReferenceException auslösen. Die zur Behebung dieses Problems verwendeten Ausdrücke sind komplizierter als eine einfache Nullprüfung und werden später in dieser Reihe behandelt.

Zuweisen, Hinzufügen und Entfernen von Aufrufzielen

So wird ein Delegattyp definiert, und Delegatinstanzen werden deklariert und aufgerufen.

Entwickler, die die List.Sort() Methode verwenden möchten, müssen eine Methode definieren, deren Signatur der Delegattypdefinition entspricht, und sie dem Delegaten zuweisen, der von der Sortiermethode verwendet wird. Diese Aufgabe fügt die Methode zur Aufrufliste des Delegatenobjekts hinzu.

Angenommen, Sie wollten eine Liste von Zeichenfolgen nach deren Länge sortieren. Ihre Vergleichsfunktion kann wie folgt aussehen:

private static int CompareLength(string left, string right) =>
    left.Length.CompareTo(right.Length);

Die Methode wird als private Methode deklariert. Das ist in Ordnung. Sie möchten möglicherweise nicht, dass diese Methode Teil ihrer öffentlichen Schnittstelle ist. Sie kann weiterhin als Vergleichsmethode verwendet werden, wenn sie an einen Delegaten angefügt ist. Der aufrufende Code wird diese Methode an die Zielliste des Delegatenobjekts angefügt und kann über diesen Delegaten darauf zugreifen.

Sie erstellen diese Beziehung, indem Sie diese Methode an die List.Sort() Methode übergeben:

phrases.Sort(CompareLength);

Beachten Sie, dass der Methodenname ohne Klammern verwendet wird. Die Verwendung der Methode als Argument weist den Compiler an, den Methodenverweis in einen Verweis zu konvertieren, der als Stellvertretungsaufrufziel verwendet werden kann, und diese Methode als Aufrufziel anfügen.

Sie könnten auch explizit gewesen sein, indem Sie eine Variable vom Typ Comparison<string> deklarieren und eine Zuweisung ausführen:

Comparison<string> comparer = CompareLength;
phrases.Sort(comparer);

Bei Verwendungen, in denen eine kleine Methode als Stellvertretungsziel genutzt wird, ist es üblich, die Lambda-Ausdruckssyntax zur Zuweisung zu verwenden.

Comparison<string> comparer = (left, right) => left.Length.CompareTo(right.Length);
phrases.Sort(comparer);

Die Verwendung von Lambda-Ausdrücken für Stellvertretungsziele wird in einem späteren Abschnitt behandelt.

Im "Sort()"-Beispiel wird normalerweise eine einzelne Zielmethode dem Delegaten zugeordnet. Stellvertretungsobjekte unterstützen jedoch Aufruflisten mit mehreren Zielmethoden, die einem Delegatenobjekt zugeordnet sind.

Delegat- und MulticastDelegate-Klassen

Die oben beschriebene Sprachunterstützung bietet die Features und Unterstützung, die Sie normalerweise für die Arbeit mit Stellvertretungen benötigen. Diese Features basieren auf zwei Klassen im .NET Core Framework: Delegate und MulticastDelegate.

Die System.Delegate Klasse und ihre einzelne direkte Unterklasse System.MulticastDelegatebieten die Framework-Unterstützung für das Erstellen von Stellvertretungen, das Registrieren von Methoden als Stellvertretungsziele und das Aufrufen aller Methoden, die als Stellvertretungsziel registriert sind.

Interessanterweise sind die System.Delegate und System.MulticastDelegate Klassen selbst keine Delegattypen. Sie stellen die Basis für alle spezifischen Delegattypen bereit. Dieser Sprachentwurfsprozess hat festgelegt, dass Sie keine Klasse deklarieren können, die von Delegate oder MulticastDelegate aus abgeleitet wird. Die C#-Sprachregeln verbieten sie.

Stattdessen erstellt der C#-Compiler Instanzen einer Klasse, die von MulticastDelegate abgeleitet ist, wenn Sie das C#-Schlüsselwort verwenden, um Delegatentypen zu deklarieren.

Dieses Design hat seine Wurzeln in der ersten Version von C# und .NET. Ein Ziel des Entwurfsteams war es, sicherzustellen, dass die Sicherheit der Sprache beim Verwenden von Stellvertretungen durch die Sprache erzwungen wurde. Dies bedeutete, dass Delegierte mit dem richtigen Typ und der richtigen Anzahl von Argumenten aufgerufen wurden. Und, dass alle Rückgabetypen zur Kompilierungszeit korrekt angegeben wurden. Delegates gehörten zur .NET-Version 1.0, die vor der Einführung von Generics lag.

Die beste Möglichkeit, diese Typsicherheit zu erzwingen, war für den Compiler das Erstellen der konkreten Delegatenklassen, die die verwendete Methodensignatur darstellten.

Obwohl Sie abgeleitete Klassen nicht direkt erstellen können, verwenden Sie die für diese Klassen definierten Methoden. Sehen wir uns die am häufigsten verwendeten Methoden an, die Sie beim Arbeiten mit Stellvertretungen verwenden werden.

Die erste und wichtigste Tatsache, die Sie beachten sollten, ist, dass jeder Delegierte, mit dem Sie arbeiten, von MulticastDelegate abgeleitet wird. Ein Multicast-Delegat ermöglicht das Ausführen mehrerer Methoden, wenn über den Delegat aufgerufen wird. Der ursprüngliche Entwurf berücksichtigte eine Unterscheidung zwischen Delegaten, bei denen nur eine Zielmethode angefügt und aufgerufen werden konnte, und Stellvertretungen, bei denen mehrere Zielmethoden angefügt und aufgerufen werden könnten. Diese Unterscheidung erwies sich als weniger nützlich in der Praxis als ursprünglich gedacht. Die beiden verschiedenen Klassen wurden bereits erstellt und sind seit der ersten öffentlichen Veröffentlichung im Framework enthalten.

Die Methoden, die Sie am häufigsten mit Stellvertretungen verwenden werden, sind Invoke() und BeginInvoke() / EndInvoke(). Invoke() ruft alle Methoden auf, die an eine bestimmte Delegateninstanz angefügt wurden. Wie Sie oben gesehen haben, rufen Sie in der Regel Delegaten mithilfe der Methodenaufrufssyntax für die Delegatvariable auf. Wie Sie später in dieser Reihe sehen, gibt es Muster, die direkt mit diesen Methoden funktionieren.

Nachdem Sie nun die Sprachsyntax und die Klassen gesehen haben, die Delegaten unterstützen, lassen Sie uns untersuchen, wie stark typisierte Delegaten verwendet, erstellt und aufgerufen werden.

Nächster