Verwenden von Delegaten (C#-Programmierhandbuch)

Ein Delegat ist ein Typ, der ähnlich einem Funktionszeiger in C und C++ eine Methode sicher kapselt. Im Gegensatz zu C-Funktionszeigern sind Delegate objektorientiert, typsicher und sicher. Der Typ eines Delegaten wird durch den Namen des Delegaten definiert. Im folgenden Beispiel wird ein Delegat mit dem Namen Callback deklariert, der eine Methode kapseln kann, die eine Zeichenfolge als Argument übernimmt und void zurückgibt:

public delegate void Callback(string message);

Ein Delegatobjekt wird normalerweise durch Angabe des Namens der Methode, die der Delegat umschließt, oder mit einem Lambdaausdruck erstellt. Sobald eine Stellvertretung auf diese Weise instanziiert wird, kann sie aufgerufen werden. Durch Aufrufen eines Delegaten wird die Methode aufgerufen, die der Delegateninstanz zugeordnet ist. Die vom Aufrufer an den Delegaten übergebenen Parameter werden an die Methode übergeben, und der Rückgabewert von der Methode wird ggf. durch den Delegaten an den Aufrufer zurückgegeben. Beispiel:

// Create a method for a delegate.
public static void DelegateMethod(string message)
{
    Console.WriteLine(message);
}
// Instantiate the delegate.
Callback handler = DelegateMethod;

// Call the delegate.
handler("Hello World");

Delegattypen werden von der Delegate-Klasse in .NET abgeleitet. Delegattypen sind versiegelt – von Ihnen kann nicht abgeleitet werden – und es ist nicht möglich benutzerdefinierte Klassen von Delegate abzuleiten. Da der instanziierte Delegat ein Objekt ist, kann er als Argument übergeben oder einer Eigenschaft zugewiesen werden. Dies ermöglicht es einer Methode, einen Delegaten als Parameter zu akzeptieren und den Delegaten zu einem späteren Zeitpunkt aufzurufen. Dies wird als asynchroner Rückruf bezeichnet und ist eine häufig verwendete Methode, um einen Aufrufer darüber zu benachrichtigen, dass ein langer Prozess abgeschlossen wurde. Wenn ein Delegat auf diese Weise verwendet wird, benötigt der Code, der den Delegaten verwendet, keine Kenntnisse über die Implementierung der verwendeten Methode. Die Funktion ähnelt den bereitgestellten Kapselungsschnittstellen.

Ein weiterer häufiger Einsatzbereich von Rückrufen ist die Definition einer benutzerdefinierten Vergleichsmethode und die Übergabe dieses Delegaten an eine Sortiermethode. Dadurch kann der Code des Aufrufers Teil des Sortieralgorithmus werden. Im folgenden Beispiel wird der Typ Del als Parameter verwendet:

public static void MethodWithCallback(int param1, int param2, Callback callback)
{
    callback("The number is: " + (param1 + param2).ToString());
}

Sie können anschließend den oben erstellten Delegaten an diese Methode übergeben:

MethodWithCallback(1, 2, handler);

woraufhin die folgende Ausgabe auf der Konsole angezeigt wird:

The number is: 3

Wenn Sie den Delegaten als Abstraktion verwenden, muss MethodWithCallback die Konsole nicht direkt aufrufen, d. h., bei der Entwicklung muss keine Konsole berücksichtigt werden. MethodWithCallback bereitet einfach eine Zeichenfolge vor und übergibt sie an eine andere Methode. Dies ist besonders leistungsstark, da eine delegierte Methode eine beliebige Anzahl Parameter verwenden kann.

Wenn ein Delegat erstellt wurde, um eine Instanzenmethode zu umschließen, verweist der Delegat sowohl auf die Instanz als auch auf die Methode. Ein Delegat kennt nicht den Instanzentyp, abgesehen von der umschlossenen Methode, sodass ein Delegat auf jede Art von Objekt verweisen kann, sofern es eine Methode für das Objekt gibt, die mit der Signatur des Delegaten übereinstimmt. Wenn ein Delegat erstellt wurde, um eine statische Methode zu umschließen, verweist er nur auf die Methode. Betrachten Sie hierzu die folgenden Deklarationen:

public class MethodClass
{
    public void Method1(string message) { }
    public void Method2(string message) { }
}

Zusammen mit der zuvor dargestellten statischen DelegateMethod verfügen Sie jetzt über drei Methoden, die von einer Del-Instanz umschlossen werden können.

Ein Delegat kann bei Aufruf mehr als eine Methode aufrufen. Dies wird als Multicasting bezeichnet. Um der Liste an Methoden des Delegaten, sprich der Aufrufliste, eine weitere Methode hinzuzufügen, müssen lediglich zwei Delegaten mithilfe der Additions- oder Additionszuweisungsoperatoren ('+' oder '+=') hinzugefügt werden. Zum Beispiel:

var obj = new MethodClass();
Callback d1 = obj.Method1;
Callback d2 = obj.Method2;
Callback d3 = DelegateMethod;

//Both types of assignment are valid.
Callback allMethodsDelegate = d1 + d2;
allMethodsDelegate += d3;

Zu diesem Zeitpunkt enthält allMethodsDelegate drei Methoden in der Aufrufliste: Method1, Method2 und DelegateMethod. Die ursprünglichen drei Delegaten, d1, d2 und d3, bleiben unverändert. Wenn allMethodsDelegate aufgerufen wird, werden alle drei Methoden nacheinander aufgerufen. Wenn der Delegat Verweisparameter verwendet, wird der Verweis wiederum nacheinander an jede der drei Methoden übergeben, und alle Änderungen einer Methode sind für die nächste Methode sichtbar. Wenn eine der Methoden eine Ausnahme auslöst, die nicht innerhalb der Methode abgefangen wird, wird diese Ausnahme an den Aufrufer des Delegaten übergeben und keine der nachfolgenden Methoden in der Aufrufliste wird aufgerufen. Wenn der Delegat über einen Rückgabewert und/oder out-Parameter verfügt, gibt er den Rückgabewert und die Parameter der letzten aufgerufenen Methode zurück. Um eine Methode aus der Aufrufliste zu entfernen, verwenden Sie die Subtraktions- oder Subtraktionszuweisungsoperatoren (- oder -=). Zum Beispiel:

//remove Method1
allMethodsDelegate -= d1;

// copy AllMethodsDelegate while removing d2
Callback oneMethodDelegate = allMethodsDelegate - d2;

Da Delegattypen von System.Delegate abgeleitet werden, können die Methoden und Eigenschaften, die durch diese Klasse definiert werden, für den Delegaten aufgerufen werden. Beispiel: Schreiben Sie Folgendes, um die Anzahl der Methoden in der Aufrufliste eines Delegaten zu ermitteln:

int invocationCount = d1.GetInvocationList().GetLength(0);

Delegaten mit mehr als einer Methode in der Aufrufliste werden von MulticastDelegate, einer Unterklasse von System.Delegate, abgeleitet. Der obige Code funktioniert in jedem Fall, da beide Klassen GetInvocationList unterstützen.

Multicastdelegaten werden ausgiebig bei der Ereignisbehandlung verwendet. Ereignisquellobjekte senden Ereignisbenachrichtigungen an Empfängerobjekte, die für den Erhalt dieses Ereignisses registriert wurden. Um sich für ein Ereignis zu registrieren, erstellt der Empfänger eine Methode zur Behandlung des Ereignisses, dann erstellt er einen Delegaten für die Methode und übergibt den Delegaten an die Ereignisquelle. Die Quelle ruft den Delegaten auf, wenn das Ereignis eintritt. Der Delegat ruft dann die Ereignisbehandlungsmethode für den Empfänger auf und übermittelt die Ereignisdaten. Der Delegattyp für ein bestimmtes Ereignis wird von der Ereignisquelle definiert. Weitere Informationen finden Sie unter Ereignisse.

Beim Vergleichen von zwei unterschiedlichen zugewiesenen Typen zur Kompilierzeit kommt es zu einem Kompilierungsfehler. Falls die Delegatinstanzen statisch vom Typ System.Delegate sind, dann ist der Vergleich zulässig, gibt jedoch zur Laufzeit "False" zurück. Beispiel:

delegate void Callback1();
delegate void Callback2();

static void method(Callback1 d, Callback2 e, System.Delegate f)
{
    // Compile-time error.
    //Console.WriteLine(d == e);

    // OK at compile-time. False if the run-time type of f
    // is not the same as that of d.
    Console.WriteLine(d == f);
}

Weitere Informationen