Freigeben über


Kommunikation zwischen lose gekoppelten Komponenten

Tipp

Diese Inhalte sind ein Auszug aus dem eBook „Enterprise Application Patterns Using .NET MAUI“, verfügbar unter .NET Docs oder als kostenlos herunterladbare PDF-Datei, die offline gelesen werden kann.

Enterprise Application Patterns Using .NET MAUI (Miniaturansicht eBook-Deckblatt).

Das Veröffentlichen-Abonnieren-Muster ist ein Messagingmuster, bei dem Herausgeber Nachrichten senden, ohne Empfänger zu kennen, die als Abonnenten bezeichnet werden. Auf ähnliche Weise lauschen Abonnenten auf bestimmte Nachrichten, ohne Herausgeber zu kennen.

Ereignisse in .NET implementieren das Veröffentlichen-Abonnieren-Muster und sind der einfachste Ansatz für eine Kommunikationsschicht zwischen Komponenten, wenn keine lose Kopplung erforderlich ist, wie beispielsweise ein Steuerelement und die Seite, die es enthält. Die Lebensdauer von Herausgeber und Abonnent sind jedoch durch Objektverweise miteinander gekoppelt, und der Abonnententyp muss einen Verweis auf den Herausgebertypen haben. Dies kann zu Problemen bei der Speicherverwaltung führen, insbesondere wenn es kurzlebige Objekte gibt, die ein Ereignis eines statischen oder langlebigen Objekts abonnieren. Wenn der Ereignishandler nicht entfernt wird, bleibt der Abonnent durch den Verweis darauf im Herausgeber erhalten, was die Garbage Collection des Abonnenten verhindert oder verzögert.

Einführung in MVVM-Toolkit-Messenger

Die IMessenger-Schnittstelle des MVVM-Toolkits beschreibt das Veröffentlichen-Abonnieren-Muster und ermöglicht so eine nachrichtenbasierte Kommunikation zwischen Komponenten, für die eine Verknüpfung über Objekt- und Typverweise ungünstig ist. Dieser Mechanismus ermöglicht die Kommunikation zwischen Herausgebern und Abonnenten, ohne dass ein direkter Verweis aufeinander besteht, und trägt dazu bei, die Abhängigkeiten zwischen Komponenten zu verringern. Zudem können Komponenten unabhängig voneinander entwickelt und getestet werden.

Hinweis

Der MVVM-Toolkit-Messenger ist Bestandteil des CommunityToolkit.Mvvm-Pakets. Informationen zum Hinzufügen des Pakets zu Ihrem Projekt finden Sie unter Einführung in das MVVM-Toolkit im Microsoft Developer Center.

Warnung

.NET MAUI enthält eine integrierte MessagingCenter-Klasse, deren Verwendung nicht mehr empfohlen wird. Verwenden Sie stattdessen den MVVM-Toolkit-Messenger.

Die IMessenger-Schnittstelle ermöglicht Multicast-Funktionen zum Veröffentlichen und Abonnieren. Dies bedeutet, dass mehrere Herausgeber vorhanden sein können, die eine einzelne Nachricht veröffentlichen, und es können mehrere Abonnenten vorhanden sein, die auf dieselbe Nachricht lauschen. Die folgende Abbildung zeigt diese Beziehung:

Multicast-Funktionen zum Veröffentlichen und Abonnieren.

Es gibt zwei Implementierungen der IMessenger-Schnittstelle, die im CommunityToolkit.Mvvm-Paket enthalten sind. WeakReferenceMessenger verwendet schwache Verweise, was zu einer einfacheren Bereinigung für Nachrichtenabonnenten führen kann. Dies ist eine gute Option, wenn Ihre Abonnenten keinen klar definierten Lebenszyklus haben. StrongReferenceMessenger verwendet starke Verweise, die zu einer besseren Leistung und einer klarer kontrollierten Lebensdauer des Abonnements führen können. Wenn Sie über einen Workflow mit einer sehr kontrollierten Lebensdauer verfügen (z. B. ein Abonnement, das an die Methoden OnAppearing und OnDisappearing einer Seite gebunden ist), ist StrongReferenceManager möglicherweise die bessere Option, wenn die Leistung ein Problem darstellt. Beide Implementierungen sind sofort einsatzbereit in Standardimplementierungen verfügbar, indem entweder auf WeakReferenceMessenger.Default oder StrongReferenceMessenger.Default verwiesen wird.

Hinweis

Die IMessenger-Klasse ermöglicht zwar die Kommunikation zwischen lose gekoppelten Klassen, bietet aber nicht die einzige Architekturlösung für dieses Problem. Beispielsweise kann die Kommunikation zwischen einem Ansichtsmodell und einer Ansicht auch durch die Bindungs-Engine und Benachrichtigungen über Eigenschaftsänderungen erreicht werden. Darüber hinaus kann die Kommunikation zwischen zwei Ansichtsmodellen auch durch die Übergabe von Daten während der Navigation erreicht werden.

Die eShop-Multiplattform-App verwendet die WeakReferenceMessenger-Klasse für die Kommunikation zwischen lose gekoppelten Komponenten. Die App definiert eine einzelne Nachricht namens AddProductMessage. Die AddProductMessage wird von der CatalogViewModel-Klasse veröffentlicht, wenn ein Element dem Warenkorb hinzugefügt wird. Im Gegenzug abonniert die CatalogView-Klasse die Nachricht und verwendet diese, um als Antwort die Produkthinzufügung mittels Animation hervorzuheben.

In der eShop-Multiplattform-App wird WeakReferenceMessenger verwendet, um die Benutzeroberfläche als Reaktion auf eine Aktion in einer anderen Klasse zu aktualisieren. Aus diesem Grund werden Nachrichten aus dem Thread veröffentlicht, in dem die Klasse ausgeführt wird, wobei Abonnenten die Nachricht im selben Thread empfangen.

Tipp

Marshallen Sie in die Benutzeroberfläche oder den Hauptthread, wenn Benutzeroberflächenupdates ausgeführt werden. Wenn Aktualisierungen an Benutzeroberflächen nicht in diesem Thread vorgenommen werden, kann dies dazu führen, dass die Anwendung abstürzt oder instabil wird.

Wenn eine Nachricht, die von einem Hintergrundthread gesendet wird, zum Aktualisieren der Benutzeroberfläche erforderlich ist, verarbeiten Sie die Nachricht im UI-Thread im Abonnenten, indem Sie die MainThread.BeginInvokeOnMainThread-Methode aufrufen.

Weitere Informationen zu Messenger finden Sie unter Messenger im Microsoft Developer Center.

Definieren einer Nachricht

IMessenger-Nachrichten sind benutzerdefinierte Objekte, die benutzerdefinierte Nutzlasten bereitstellen. Das folgende Codebeispiel zeigt die in der eShop-Multiplattform-App definierte AddProductMessage-Nachricht:

public class AddProductMessage : ValueChangedMessage<int>
{
    public AddProductMessage(int count) : base(count)
    {
    }
}

Die Basisklasse wird mit ValueChangedMessage<T> definiert, wobei T ein beliebiger Typ sein kann, der zum Übergeben von Daten erforderlich ist. Sowohl Herausgeber als auch Abonnenten von Nachrichten können Nachrichten eines bestimmten Typs erwarten (z. B AddProductMessage). Dies kann dazu beitragen, sicherzustellen, dass beide Parteien einem Messagingvertrag zugestimmt haben und dass die in diesem Vertrag bereitgestellten Daten konsistent sind. Darüber hinaus bietet dieser Ansatz Unterstützung für Typsicherheit zur Kompilierzeit und Refactoring.

Veröffentlichen einer Nachricht

Um eine Nachricht zu veröffentlichen, müssen wir die IMessenger.Send-Methode verwenden. Auf diese kann am häufigsten über WeakReferenceMessenger.Default.Send oder StrongReferenceMessenger.Default.Send zugegriffen werden. Die gesendete Nachricht kann einen beliebigen Objekttyp aufweisen. Im folgenden Codebeispiel wird gezeigt, wie die AddProduct-Nachricht veröffentlicht wird:

WeakReferenceMessenger.Default.Send(new Messages.AddProductMessage(BadgeCount));

In diesem Beispiel stellt die angegebene Send--Methode eine neue Instanz des AddProductMessage-Objekts für bereit, das Downstreamabonnenten empfangen sollen. Ein zusätzlicher zweiter Tokenparameter kann hinzugefügt werden, der verwendet werden kann, wenn mehrere verschiedene Abonnenten Nachrichten desselben Typs empfangen müssen, ohne die falsche Nachricht zu erhalten.

Die Send-Methode veröffentlicht die Nachricht sowie alle ihre Nutzdaten unter Verwendung eines Fire-and-Forget-Ansatzes. Daher wird die Nachricht auch dann gesendet, wenn für den Empfang der Nachricht keine Abonnenten registriert sind. Unter diesen Umständen wird die gesendete Nachricht ignoriert.

Abonnieren einer Nachricht

Abonnenten können sich registrieren, um eine Nachricht mit einer der IMessenger.Register<T>-Überladungen zu empfangen. Im folgenden Codebeispiel wird gezeigt, wie die eShop-Multiplattform-App die AddProductMessage-Nachricht abonniert und verarbeitet:

WeakReferenceMessenger.Default
    .Register<CatalogView, Messages.AddProductMessage>(
        this,
        async (recipient, message) =>
        {
            await recipient.Dispatcher.DispatchAsync(
                async () =>
                {
                    await recipient.badge.ScaleTo(1.2);
                    await recipient.badge.ScaleTo(1.0);
                });
        });

Im vorangegangenen Beispiel abonniert die Register-Methode die AddProductMessage-Nachricht und führt als Reaktion auf den Empfang der Nachricht einen Rückrufdelegaten aus. Dieser Rückrufdelegat, der als Lambdaausdruck angegeben wird, führt Code aus, der die Benutzeroberfläche aktualisiert.

Hinweis

Vermeiden Sie die Verwendung von this innerhalb Ihres Rückrufdelegaten, um zu vermeiden, dass dieses Objekt innerhalb des Delegaten erfasst wird. Dies kann dabei helfen, die Leistung zu verbessern. Verwenden Sie stattdessen den Parameter recipient.

Wenn Nutzdaten bereitgestellt werden, versuchen Sie nicht, die Nutzdaten aus einem Rückrufdelegaten zu ändern, da mehrere Threads gleichzeitig auf die empfangenen Daten zugreifen können. In diesem Szenario sollten die Nutzdaten unveränderlich sein, um Parallelitätsfehler zu vermeiden.

Kündigen des Abonnements einer Nachricht

Abonnenten können Nachrichten kündigen, die Sie nicht mehr erhalten möchten. Dies wird mit einer der IMessenger.Unregister-Überladungen erreicht, wie im folgenden Codebeispiel gezeigt:

WeakReferenceMessenger.Default.Unregister<Messages.AddProductMessage>(this);

Hinweis

In diesem Beispiel ist es nicht vollständig erforderlich, Unregister aufzurufen, da der WeakReferenceMessenger die Garbage Collection von nicht verwendeten Objekten zulässt. Wenn StrongReferenceMessenger verwendet würde, empfiehlt es sich, Unregister für alle Abonnements aufzurufen, die nicht mehr verwendet werden.

In diesem Beispiel gibt die Unsubscribe-Methodensyntax das type-Argument der Nachricht an sowie das Empfängerobjekt, das auf Nachrichten lauscht.

Zusammenfassung

Die IMessenger-Schnittstelle des MVVM-Toolkits beschreibt das Veröffentlichen-Abonnieren-Muster und ermöglicht so eine nachrichtenbasierte Kommunikation zwischen Komponenten, für die eine Verknüpfung über Objekt- und Typverweise ungünstig ist. Dieser Mechanismus ermöglicht die Kommunikation zwischen Herausgebern und Abonnenten, ohne dass ein Verweis aufeinander besteht, und trägt dazu bei, die Abhängigkeiten zwischen Komponenten zu verringern. Zudem können Komponenten unabhängig voneinander entwickelt und getestet werden.