Untersuchen der späten Bindung mit Delegaten
In C# werden Methoden zum Ausführen von Aktionen verwendet, z. B. zum Ausführen einer Berechnung oder zum Abrufen von Informationen. Ihre Anwendungen verwenden Methoden, um beabsichtigte Ergebnisse zu erzielen und die Methoden aufzurufen, wenn bestimmte Bedingungen erfüllt sind. Anwendungen automatisieren Prozesse häufig, indem sie eine Methode aufrufen, die wiederum eine weitere Methode aufrufen kann, und so weiter. Da die Bedingungen zur Kompilierungszeit bekannt sind, implementiert der ausführbare Code vordefinierte Workflows auf vorhersehbare Weise. Sie können beispielsweise eine Methode haben, die einen monatlichen Kundenbericht generiert, wenn auf eine Schaltfläche geklickt wird. Wenn der Code ausgeführt wird und auf die Schaltfläche geklickt wird, wird die Methode aufgerufen, und der Bericht wird generiert. Dieses Szenario ist ein Beispiel für eine frühe Bindung, bei der die aufgerufene Methode zur Kompilierungszeit bestimmt wird. Frühe Bindung eignet sich hervorragend für viele Szenarien, da der Code leicht zu lesen und zu verstehen ist. Sie können den Fluss des Programms sehen, indem Sie den Methodenaufrufen folgen. Der Code ist vorhersehbar, und Sie wissen genau, welche Methode aufgerufen wird, wenn auf die Schaltfläche geklickt wird.
Es gibt jedoch Situationen, in denen Sie nicht wissen, welche Methode aufgerufen werden soll, bis das Programm ausgeführt wird. Angenommen, Sie arbeiten an einer App, die auf Laufzeitabhängigkeiten basiert, um zu entscheiden, welche Methode zum Analysieren von Kundendaten erforderlich ist. Dieses Szenario ist ein Beispiel für eine späte Bindung, bei der die aufgerufene Methode zur Laufzeit bestimmt wird. In diesem Fall kann die aufgerufene Methode von einer Eingabe eines externen Ressourcenbenutzers oder vom aktuellen Zustand der Anwendung abhängen.
In C# werden Delegaten verwendet, um späte Bindung zu implementieren.
Was ist eine Stellvertretung?
Ein Delegat ist ein .NET-Typ, der von der Delegate Klasse abgeleitet und zum Kapseln von Methoden verwendet wird. Indem Sie Methoden kapseln und als Objekte instanziieren, können Sie mit Delegaten Methoden in Variablen speichern, sie als Argumente an andere Methoden übergeben und zu einem späteren Zeitpunkt aufrufen.
Die Signatur eines Delegaten definiert die Parameter und gibt den Typ der Methoden zurück, die ihm zugewiesen werden können. Dies bedeutet, dass Sie einen Delegattyp erstellen können, der mit der Signatur einer Methode übereinstimmt, und dann eine beliebige Methode mit dieser Signatur dem Delegaten zuweisen. Mit dieser Funktion können Sie die Methode über den Delegaten aufrufen, auch wenn Sie nicht wissen, welche Methode sie zur Kompilierungszeit ist.
Das folgende Beispiel zeigt, wie ein Delegattyp definiert wird:
public delegate int PerformCalculation(int x, int y);
In diesem Beispiel wird eine Stellvertretung mit dem Namen PerformCalculationdefiniert. Der PerformCalculation Delegat ist definiert, um Methoden darzustellen, die zwei int Parameter (x und y) verwenden und einen intWert zurückgeben. Dieser Delegat kann zum Kapseln von Methoden verwendet werden, die unterschiedliche Berechnungen ausführen, z. B. Addition, Subtraktion, Multiplikation, Division oder komplexere Formeln, die die beiden Parameter verwenden.
Beispielsweise kann dieser Delegat verwendet werden, um die folgenden Methoden zu kapseln:
public class Calculator
{
public int Add(int x, int y)
{
return x + y;
}
public int Subtract(int x, int y)
{
return x - y;
}
public int Multiply(int x, int y)
{
return x * y;
}
public int Divide(int x, int y)
{
if (y == 0)
throw new DivideByZeroException();
return x / y;
}
}
In diesem Beispiel wird eine Calculator Klasse definiert, die Methoden enthält, die der Signatur des PerformCalculation Delegaten entsprechen. Sie können Instanzen des Delegaten erstellen und sie verschiedenen Methoden zuweisen, sodass Sie die entsprechende Methode zur Laufzeit basierend auf Ihren Anforderungen aufrufen können.
Der folgende Code zeigt, wie der PerformCalculation Delegat verwendet wird:
public class Program
{
public static void Main()
{
Calculator calculator = new Calculator();
// Create delegate instances
PerformCalculation add = new PerformCalculation(calculator.Add);
PerformCalculation subtract = new PerformCalculation(calculator.Subtract);
PerformCalculation multiply = new PerformCalculation(calculator.Multiply);
PerformCalculation divide = new PerformCalculation(calculator.Divide);
// Call the methods using the delegates
Console.WriteLine("Addition: " + add(5, 3)); // Output: 8
Console.WriteLine("Subtraction: " + subtract(5, 3)); // Output: 2
Console.WriteLine("Multiplication: " + multiply(5, 3)); // Output: 15
Console.WriteLine("Division: " + divide(5, 3)); // Output: 1
}
}
In diesem Beispiel wird eine Instanz der Calculator Klasse erstellt und die Methoden werden Instanzen des PerformCalculation Delegaten zugewiesen. Die Methoden werden dann mithilfe der Delegateninstanzen aufgerufen, sodass Sie unterschiedliche Berechnungen basierend auf der zugewiesenen Stellvertretung durchführen können.
Merkmale des Delegattyps
Delegaten sind Referenztypen, was bedeutet, dass sie auf dem Heap gespeichert sind und wie jedes andere Objekt übergeben werden können. Wenn Sie eine Stellvertretungsinstanz erstellen, erstellen Sie ein Objekt, das eine Methode kapselt. Mit dieser Kapselung können Sie den Delegat wie jedes andere Objekt in C# als Erstklassiges Objekt behandeln. Sie können Instanzen von Delegaten erstellen, variablen zuweisen und als Argumente an Methoden übergeben. Der Prozess ähnelt der Arbeit mit anderen Objekten in C#, z. B. Zeichenfolgen oder Listen.
Stellvertretungstypen sind versiegelt, können nicht abgeleitet werden, und es ist nicht möglich, benutzerdefinierte Klassen von der Delegate Klasse abzuleiten. Delegaten sind auch unveränderlich, d. h., dass Sie nach der Erstellung eines Delegaten nicht mehr die Methode ändern können, die sie kapselt. Sie können jedoch neue Delegateninstanzen erstellen, die auf verschiedene Methoden verweisen. Durch diese Unveränderlichkeit wird sichergestellt, dass der Delegat immer auf dieselbe Methode verweist, die für die Aufrechterhaltung der Integrität des Codes wichtig ist.
Sie rufen eine Delegatinstanz wie eine Methode auf, und sie ruft die Methode auf, die sie kapselt.
Stellvertretungen weisen die folgenden Merkmale auf:
- Mit Delegates können Sie Methoden als Argumente an andere Methoden übergeben.
- Delegaten können miteinander verkettet werden, z. B. das Aufrufen mehrerer Methoden für ein einzelnes Ereignis.
- Delegaten sind typsicher, d. h., der Compiler überprüft, ob eine Methodensignatur zur Kompilierungszeit mit der Signatur des Delegats übereinstimmt.
Hinweis
Methoden müssen nicht exakt mit dem Delegattyp übereinstimmen. Wenn die Methodensignatur kompatibel ist, kann der Compiler die Methode dem Delegat zuweisen. Dieses Verhalten wird als Kovarianz und Kontravarianz bezeichnet. Mit der Kovarianz können Sie einen stärker abgeleiteten Typ verwenden als ursprünglich angegeben, während bei der Kontravarianz ein weniger abgeleiteter Typ verwendet werden kann. Diese Funktion ist nützlich beim Arbeiten mit Vererbung und Polymorphismus, da Sie flexibleren und wiederverwendbaren Code erstellen können. Die Varianz wird in einer separaten Einheit dieses Moduls untersucht.
Warum Stellvertretungen verwenden?
Delegaten bieten gegenüber direkten Methodenaufrufen mehrere Vorteile und lösen verschiedene wichtige Probleme im Zusammenhang mit Codeflexibilität, dynamischen Methodenaufrufen und Typsicherheit.
Stellvertretungen bieten viele Vorteile, einschließlich der folgenden Elemente:
- Flexibilität: Stellvertretungen ermöglichen es Ihnen, verschiedene Methoden als Parameter zu übergeben und das dynamische Verhalten basierend auf Laufzeitbedingungen zu ermöglichen. Diese Flexibilität ist nützlich, wenn der genaue auszuführende Vorgang zur Laufzeit bestimmt wird.
- Erweiterbarkeit: Mit Delegaten können Sie die Funktionalität ganz einfach erweitern, indem Sie neue Vorgänge hinzufügen, ohne den vorhandenen Code zu ändern. Sie können jede Methode übergeben, die der Signatur des Delegats entspricht.
- Decoupling: Delegaten decoupieren die Methodenaufrufe von der Methodendefinition, wodurch der Code modularer und einfacher verwaltet werden kann.
Stellvertretungen sind ein leistungsfähiges Feature von C#, das mehrere häufige Probleme bei der Programmierung löst. Sie bieten eine flexible und typsichere Möglichkeit zum Kapseln von Methoden, wodurch dynamische Methodenaufrufe, Rückrufmethoden und Multicastaufrufe ermöglicht werden. Mithilfe von Delegaten können Sie wiederverwendbaren und besser wartbaren Code schreiben, der sich an sich ändernde Anforderungen anpassen kann.
Dynamischer Methodenaufruf
Die Möglichkeit, Methoden zur Laufzeit dynamisch aufzurufen, ist ein leistungsfähiges Feature von Delegaten. Mit dynamischen Methodenaufrufen können Sie flexibleren und wiederverwendbaren Code schreiben, da Sie verschiedene Methoden als Parameter übergeben können, ohne ihre genaue Implementierung zur Kompilierungszeit zu kennen.
Szenario: Stellen Sie sich vor, Sie haben eine Liste von Kunden, und Sie müssen sie nach verschiedenen Kriterien sortieren, z. B. Name, Kontotyp oder Kunden-ID. Ohne Stellvertretungen müssen Sie separate Sortiermethoden für jedes Kriterium schreiben, was zu sich wiederholendem und weniger wartungsfähigem Code führt.
Lösung: Mit Delegaten können Sie Methoden als Parameter übergeben und dynamische Methodenaufrufe ermöglichen. Sie können einen Delegaten definieren, der der Signatur der Sortiermethode entspricht, und während der Laufzeit verschiedene Vergleichsfunktionen übergeben. Dadurch wird Ihr Code flexibler und wiederverwendbar.
Rückrufmethoden
Die Notwendigkeit von Rückrufmethoden tritt in Szenarien auf, in denen Sie eine Aktion ausführen möchten, nachdem ein bestimmter Vorgang abgeschlossen wurde. Das Rückrufmuster ist bei der asynchronen Programmierung üblich, bei der Sie den Benutzer benachrichtigen oder andere Aktionen ausführen möchten, sobald eine lange ausgeführte Aufgabe abgeschlossen ist.
Szenario: Bei der asynchronen Programmierung müssen Sie häufig andere Aktionen ausführen, sobald ein Vorgang abgeschlossen ist. Ohne Delegaten müssten Sie den asynchronen Vorgang eng mit den Folgeaktionen koppeln, was die Flexibilität verringert und den Code schwieriger zu warten macht.
Lösung: Stellvertretungen ermöglichen die Implementierung von Rückrufmethoden, sodass Sie die Nachverfolgungsaktionen dynamisch angeben können. Dadurch wird der asynchrone Vorgang von der Rückruflogik entkoppelt, wodurch Flexibilität und Verwaltbarkeit verbessert werden.
Typsicherheit
Die Typsicherheit ist ein wichtiger Aspekt der Programmierung, der sicherstellt, dass die Methoden, die Sie aufrufen, den erwarteten Signaturen entsprechen. Dadurch werden Laufzeitfehler verhindert und ihr Code robuster.
Szenario: Wenn Sie Methoden als Parameter übergeben oder für späteren Aufruf speichern, müssen Sie sicherstellen, dass die Methodensignaturen übereinstimmen. Ohne Stellvertretungen würden Sie keine Typsicherheit haben, was zu potenziellen Laufzeitfehlern und weniger robustem Code führt.
Lösung: Stellvertretungen stellen typensichere Methodenverweise bereit, um sicherzustellen, dass die Methodensignaturen mit der Stellvertretungssignatur übereinstimmen. Dadurch werden Laufzeitfehler verhindert und der Code robuster.
Multicastaufruf
Multicastaufruf ist ein Feature von Delegaten, mit dem Sie mehrere Methoden mit einem einzelnen Delegaten aufrufen können. Dieses Feature ist nützlich bei Ereignisbehandlungsszenarien, in denen Sie mehrere Ereignisabonnenten über ein Vorkommen benachrichtigen möchten.
Szenario: Bei der Ereignisbehandlung müssen Sie häufig mehrere Abonnenten über ein Vorkommen benachrichtigen. Ohne Stellvertretungen müssten Sie die Liste der Abonnenten manuell verwalten und deren Methoden aufrufen, was zu komplexem und fehleranfälligen Code führt.
Lösung: Delegierte unterstützen Multicastaufrufe, sodass ein einzelner Delegat mehrere Methoden referenzieren kann. Dadurch wird die Ereignisbehandlung vereinfacht, indem die Liste der Abonnenten automatisch verwaltet und ihre Methoden in der richtigen Reihenfolge aufgerufen werden.
Bewährte Methoden zum Deklarieren von Stellvertretungen in C#
Beim Hinzufügen von Stellvertretungen zu einer Klasse ist es wichtig, bewährte Methoden zu befolgen, um sicherzustellen, dass Ihr Code organisiert, lesbar und verwaltet werden kann. Hier sind einige Richtlinien zum Definieren von Stellvertretungen:
Definieren von Stellvertretungen am Anfang einer Klassendatei
Es ist üblich, dass Entwickler Stellvertretungen am Anfang einer Datei deklarieren, in der Regel innerhalb des Namespaces, aber außerhalb einer Klasse. Dieser Ansatz erleichtert das Auffinden der Stellvertretungsdefinitionen und das Verständnis ihres Zwecks. Außerdem können Sie Delegaten definieren, die von mehreren Klassen innerhalb desselben Namespaces verwendet werden können.
Der folgende Code zeigt, wie Sie einen Delegaten implementieren, der außerhalb einer Klasse definiert ist:
namespace MyNamespace
// Define the delegate outside the class
public delegate void MyDelegate(string message);
public class Publisher
{
// Method that uses the delegate
public void PublishMessage(MyDelegate del)
{
del("Hello from Publisher!");
}
}
public class Subscriber
{
public void Subscribe()
{
// Create an instance of the Publisher class
Publisher publisher = new Publisher();
// Create an instance of the delegate and pass a method to it
MyDelegate del = new MyDelegate(PrintMessage);
// Call the method of the Publisher class and pass the delegate
publisher.PublishMessage(del);
}
// Method that matches the delegate signature
public void PrintMessage(string message)
{
Console.WriteLine(message);
}
}
Definieren von Stellvertretungen innerhalb einer Klasse
Wenn eine Stellvertretung spezifisch für eine Klasse ist und nicht außerhalb der Klasse verwendet werden soll, können Sie den Delegaten innerhalb der Klasse definieren. Dadurch wird der Delegat innerhalb der Klasse gekapselt, was nützlich ist, wenn der Delegat eng mit der Funktionalität der Klasse verknüpft ist und nicht für die Verwendung außerhalb dieses Kontexts vorgesehen ist. Wenn der Delegat öffentlich ist, können andere Klassen innerhalb desselben Namespace mithilfe einer Instanz der Klasse auf den Delegaten zugreifen.
namespace MyNamespace
public class Publisher
{
// Define a public delegate
public delegate void MyDelegate(string message);
// Method that uses the delegate
public void PublishMessage(MyDelegate del)
{
del("Hello from Publisher!");
}
}
public class Subscriber
{
public void Subscribe()
{
// Create an instance of the Publisher class
Publisher publisher = new Publisher();
// Create an instance of the delegate and pass a method to it
Publisher.MyDelegate del = new Publisher.MyDelegate(PrintMessage);
// Call the method of the Publisher class and pass the delegate
publisher.PublishMessage(del);
}
// Method that matches the delegate signature
public void PrintMessage(string message)
{
Console.WriteLine(message);
}
}
Wichtige Punkte
- Delegaten sind .NET-Typen, die von der
DelegateKlasse abgeleitet sind, die Methoden kapseln. - Mit Delegates können Sie Methoden als Parameter übergeben, in Variablen speichern und zur Laufzeit aufrufen.
- Delegaten sind typsicher, d. h., der Compiler überprüft, ob die Methodensignaturen zur Kompilierungszeit mit der Signatur des Delegats übereinstimmen.
- Delegate können für dynamische Methodenaufrufe, Rückrufmethoden, Typensicherheit und Multicastaufrufe verwendet werden.
- Delegate sind die Grundlage für die Ereignisbehandlung in C#.
- Delegate können miteinander verkettet werden, sodass mehrere Methoden mit einem einzelnen Delegaten aufgerufen werden können.
- Stellvertretungen werden in der Regel außerhalb einer Klasse definiert, um den Zugriff von anderen Klassen zu ermöglichen, ohne die Klasse instanziieren zu müssen.