Unterscheidung zwischen Delegaten und Ereignissen

Vorherige

Entwickler, die die .NET Core-Plattform noch nicht kennen, haben oft mit der Entscheidung zwischen einem Entwurf auf der Grundlage von delegates und einem Entwurf auf der Grundlage von events zu kämpfen. Die Wahl zwischen Delegaten oder Ereignissen ist meist schwierig, da sich die beiden Sprachfeatures ähneln. Ereignisse werden sogar mithilfe der Sprachunterstützung für Delegaten erstellt.

Beide bieten ein Szenario mit später Bindung: Sie ermöglichen Szenarios, in denen eine Komponente durch Aufrufen einer Methode kommuniziert, die nur zur Laufzeit bekannt ist. Beide Versionen unterstützen einzelne und mehrerer Methoden des Abonnenten. Möglicherweise finden Sie dies unter der Bezeichnung Singlecast- und Multicast-Unterstützung. Beide unterstützen eine ähnliche Syntax zum Hinzufügen und Entfernen von Handlern. Schließlich verwenden das Auslösen eines Ereignisses und das Aufrufen eines Delegaten genau die gleiche Methodensyntax zu Aufrufen. Auch unterstützen beide die gleiche Invoke()-Methodensyntax für die Verwendung mit dem ?.-Operator.

Mit all diesen Ähnlichkeiten ist es einfach nur schwer zu bestimmen, wann welche Komponente benutzt werden soll.

Das Überwachen von Ereignissen ist optional

Der wichtigste Aspekt bei der Bestimmung, welche Sprachfunktion verwendet werden soll, ist die Frage, ob ein angefügter Abonnent vorhanden sein muss. Wenn der Code den vom Abonnenten bereitgestellten Code aufrufen muss, sollten Sie einen delegatbasierten Entwurf werden, wenn ein Callback implementiert werden muss. Wenn Ihr Code all seine Arbeit ohne Aufruf von Abonnenten abschließen kann, sollten Sie einen Entwurf auf Grundlage von Ereignissen verwenden.

Betrachten Sie die Beispiele, die während dieses Abschnitts erstellt wurden. Dem Code, den Sie mithilfe von List.Sort() erstellt haben, muss eine Vergleichsfunktion gegeben werden, um die Elemente ordnungsgemäß zu sortieren. LINQ-Abfragen müssen mit Delegaten bereitgestellt werden, um zu bestimmen, welche Elemente zurückgeben werden sollen. Beide verwenden einen mit Delegaten erstellten Entwurf.

Betrachten Sie das Progress-Ereignis. Es meldet den Status eines Tasks. Der Task wird fortgesetzt, unabhängig davon, ob alle Listener vorhanden sind. Der FileSearcher ist ein weiteres Beispiel. Es würde weiterhin alle gesuchten Dateien suchen und finden, auch ohne angefügte Ereignisabonnenten. UX-Steuerelemente funktionieren weiterhin ordnungsgemäß, auch wenn keine Abonnenten die Ereignisse überwachen. Beide verwenden Entwürfe auf der Grundlage von Ereignissen.

Rückgabewerte erfordern Delegaten

Ein weiterer Aspekt ist der Methodenprototyp, den Sie für die Delegatmethode haben sollten. Wie Sie gesehen haben, verfügen die für Ereignisse verwendete Delegaten alle über einen void-Rückgabetyp. Sie haben auch gesehen, dass Ausdrücke vorhanden sind, um Ereignishandler zu erstellen, die Informationen durch Ändern der Eigenschaften des Ereignisargumentobjekts zurück an die Ereignisquellen geben. Während diese Ausdrücke funktionieren, sind sie nicht so natürlich wie die Rückgabe eines Werts aus einer Methode.

Beachten Sie, dass häufig diese beiden Heuristiken vorhanden sein können: Wenn die Delegatmethode einen Wert zurückgibt, wirkt sich dies wahrscheinlich auf irgendeine Weise auf den Algorithmus aus.

Privater Aufruf von Ereignissen

Klassen, in denen das Ereignis nicht enthalten ist, können nur Ereignislistener hinzufügen und entfernen. Nur die Klasse, die das Ereignis enthält, kann das Ereignis aufrufen. Ereignisse sind normalerweise öffentliche Klassenmember. Delegaten hingegen werden oft als Parameter übergeben und als private Klassenmember gespeichert, sofern sie überhaupt gespeichert werden.

Ereignislistener haben häufig eine längere Lebensdauer

Dass Ereignislistener eine längere Lebensdauer besitzen, ist eine eher schlechtere Begründung. Jedoch können Sie feststellen, dass ereignisbasierte Entwürfe natürlicher sind, wenn die Ereignisquelle über einen langen Zeitraum Ereignisse auslösen wird. Beispiele für ereignisbasierte Designs für Steuerelemente der Benutzeroberfläche können Sie in vielen Systemen finden. Sobald Sie ein Ereignis abonnieren, kann die Ereignisquelle Ereignisse während der gesamten Lebensdauer des Programms auslösen. (Sie können das Abonnement von Ereignissen kündigen, wenn Sie sie nicht mehr benötigen.)

Vergleichen Sie das mit vielen delegatbasierten Entwürfen, bei denen ein Delegat als Argument an eine Methode verwendet wird, und der Delegat wird nicht verwendet, nachdem diese Methode zurückgegeben wird.

Sorgfältig bewerten

Die oben genannten Aspekte sind keine verbindlichen Regeln. Stattdessen stellen sie Leitfäden dar, mit denen Sie entscheiden können, welche Auswahl für Ihre spezielle Verwendung am besten geeignet ist. Da sie sich ähneln, können Sie sogar beide als Prototyp nutzen, und überlegen, welche beim Arbeiten natürlicher wäre. Beide behandeln Szenarios mit später Bindung gut. Verwenden Sie die, die Ihren Entwurf am besten kommuniziert.