Condividi tramite


Comunicazione tra componenti ad accoppiamento debole

Suggerimento

Questo contenuto è un estratto dell'eBook, Enterprise Application Patterns Using .NETMAUI, disponibile in .NET Docs o come PDF scaricabile gratuitamente che può essere letto offline.

Enterprise Application Patterns Using .NET MAUI eBook cover thumbnail.

Il modello di pubblicazione-sottoscrizione è un modello di messaggistica in cui i server di pubblicazione inviano messaggi senza conoscere i ricevitori, noti come sottoscrittori. In modo analogo, i sottoscrittori sono in ascolto di messaggi specifici, senza conoscere i server di pubblicazione.

Gli eventi in .NET implementano il modello di pubblicazione-sottoscrizione e rappresentano l'approccio più semplice per un livello di comunicazione tra componenti se non è necessario l'accoppiamento debole, ad esempio un controllo e la pagina che lo contiene. Tuttavia, le durate del server di pubblicazione e del sottoscrittore sono accoppiate l'una all'altra tramite riferimenti a oggetti e il tipo di sottoscrittore deve avere un riferimento al tipo di server di pubblicazione. Questo può causare problemi di gestione della memoria, in particolare quando sono presenti oggetti di breve durata che sottoscrivono un evento di un oggetto statico o di lunga durata. Se il gestore eventi non viene rimosso, il sottoscrittore verrà mantenuto attivo dal riferimento a esso nel server di pubblicazione e questo impedirà o ritarderà l'operazione di Garbage Collection del sottoscrittore.

Introduzione a MVVM Toolkit Messenger

L'interfaccia di MVVM Toolkit IMessenger descrive il modello di pubblicazione-sottoscrizione, consentendo la comunicazione basata su messaggi tra componenti che non è pratico collegare con riferimenti a oggetti e tipi. Questo meccanismo consente ai server di pubblicazione e ai sottoscrittori di comunicare senza avere un riferimento diretto l'uno all'altro, contribuendo a ridurre le dipendenze tra i componenti e consentendo lo sviluppo e il test indipendente di tali componenti.

Nota

MVVM Toolkit Messenger fa parte del pacchetto CommunityToolkit.Mvvm. Per informazioni su come aggiungere il pacchetto al progetto, vedere Introduzione a MVVM Toolkit nel Centro per sviluppatori Microsoft.

Avviso

.NET MAUI contiene una classe MessagingCenter predefinita che non è più consigliata per l'uso e che richiede quindi la migrazione a MVVM Toolkit Messenger.

L'interfaccia IMessenger consente la funzionalità di pubblicazione-sottoscrizione multicast. Ciò significa che possono essere presenti più server di pubblicazione che pubblicano un singolo messaggio e più sottoscrittori in ascolto dello stesso messaggio. L'immagine seguente illustra questa relazione:

Multicast publish-subscribe functionality.

Esistono due implementazioni dell'interfaccia IMessenger fornite con il pacchetto CommunityToolkit.Mvvm. WeakReferenceMessenger usa riferimenti deboli che garantiscono una pulizia più semplice per i sottoscrittori di messaggi. Si tratta di un'opzione valida se i sottoscrittori non hanno un ciclo di vita chiaramente definito. StrongReferenceMessenger usa riferimenti sicuri che possono garantire prestazioni migliori e una durata della sottoscrizione controllata con maggiore chiarezza. Se si dispone di un flusso di lavoro con una durata molto controllata (ad esempio, una sottoscrizione associata ai metodi OnAppearing e OnDisappearing di una pagina), StrongReferenceManager potrebbe essere un'opzione migliore, soprattutto per evitare problemi di prestazioni. Entrambe queste implementazioni sono disponibili con implementazioni predefinite pronte per l'uso facendo riferimento a WeakReferenceMessenger.Default o StrongReferenceMessenger.Default.

Nota

Sebbene l'interfaccia IMessenger consenta la comunicazione tra classi ad accoppiamento debole, non costituisce l'unica soluzione di architettura a questo problema. Ad esempio, la comunicazione tra un modello di visualizzazione e una visualizzazione può essere ottenuta anche dal motore di associazione e tramite notifiche delle modifiche delle proprietà. Inoltre, è possibile ottenere la comunicazione tra due modelli di visualizzazione passando i dati durante la navigazione.

L'app multipiattaforma eShopOnContainers usa la classe WeakReferenceMessenger per la comunicazionee tra componenti ad accoppiamento debole. L'app definisce un singolo messaggio denominato AddProductMessage. L'oggetto AddProductMessage è pubblicato dalla classe CatalogViewModel quando un elemento viene aggiunto al carrello. A sua volta, la classe CatalogView sottoscrive il messaggio e lo usa per evidenziare le aggiunte dei prodotti con un'animazione in risposta.

Nell'app multipiattaforma eShopOnContainers, WeakReferenceMessenger consente di aggiornare l'interfaccia utente in risposta a un'azione eseguita in un'altra classe. Di conseguenza, i messaggi vengono pubblicati dal thread in cui è in esecuzione la classe, con i sottoscrittori che ricevono il messaggio nello stesso thread.

Suggerimento

Effettuare il marshalling dell'interfaccia utente o del thread principale durante gli aggiornamenti dell'interfaccia utente. Se gli aggiornamenti alle interfacce utente non vengono eseguiti in questo thread, può verificarsi l'arresto anomalo dell'applicazione o questa può diventare instabile.

Se per aggiornare l'interfaccia utente è necessario un messaggio inviato da un thread in background, elaborare il messaggio nel thread dell'interfaccia utente nel sottoscrittore richiamando il metodo MainThread.BeginInvokeOnMainThread.

Per altre informazioni su Messenger, vedere Messenger nel Centro per sviluppatori Microsoft.

Definizione di un messaggio

I messaggi IMessenger sono oggetti personalizzati che forniscono payload personalizzati. L'esempio di codice seguente mostra il messaggio AddProductMessage definito nell'app multipiattaforma eShopOnContainers:

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

La classe di base viene definita usando ValueChangedMessage<T>, dove T può essere di qualsiasi tipo necessario per passare i dati. Sia per i server di pubblicazione sia per i sottoscrittori dei messaggi è prevista la ricezione messaggi di un tipo specifico, ad esempio AddProductMessage. Ciò può contribuire a garantire che entrambe le parti abbiano accettato un contratto di messaggistica e che i dati forniti con tale contratto saranno coerenti. Questo approccio offre inoltre supporto per la sicurezza e il refactoring dei tipi in fase di compilazione.

Pubblicazione di un messaggio

Per pubblicare un messaggio, sarà necessario usare il metodo IMessenger.Send. Il modo più comune di accedervi è tramite WeakReferenceMessenger.Default.Send o StrongReferenceMessenger.Default.Send. Il messaggio inviato può essere di qualsiasi tipo di oggetto. Nell'esempio di codice seguente viene mostrata la pubblicazione del messaggio AddProduct:

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

In questo esempio, il metodo Send specifica una nuova istanza dell'oggetto AddProductMessage da ricevere per i sottoscrittori downstream. È possibile aggiungere un secondo parametro di token aggiuntivo da usare quando più sottoscrittori diversi devono ricevere messaggi dello stesso tipo senza ricevere il messaggio errato.

Il metodo Send pubblicherà il messaggio e i relativi dati di payload usando un approccio "fire-and-forget". Il messaggio viene quindi inviato anche se non sono presenti sottoscrittori registrati per la ricezione del messaggio. In questa situazione, il messaggio inviato viene ignorato.

Sottoscrizione di un messaggio

I sottoscrittori possono registrarsi per ricevere un messaggio usando uno degli overload di IMessenger.Register<T>. L'esempio di codice seguente illustra come l'app multipiattaforma eShopOnContainers sottoscrive ed elabora il messaggio AddProductMessage:

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);
                });
        });

Nell'esempio precedente il metodo Register sottoscrive il messaggio AddProductMessage ed esegue un delegato di callback in risposta alla ricezione del messaggio. Questo delegato di callback, specificato come espressione lambda, esegue il codice che aggiorna l'interfaccia utente.

Nota

Evitare l'uso di this all'interno del delegato di callback per impedire l'acquisizione di tale oggetto all'interno del delegato. In questo modo è possibile migliorare le prestazioni. Usare invece il parametro recipient.

Se vengono forniti dati del payload, non tentare di modificarli dall'interno di un delegato di callback, in quanto è possibile che più thread accedano simultaneamente ai dati ricevuti. In questo scenario, i dati del payload devono essere non modificabili per evitare errori di concorrenza.

Annullamento della sottoscrizione di un messaggio

I sottoscrittori possono annullare la sottoscrizione dei messaggi che non vogliono più ricevere A tale scopo, è necessario usare uno degli overload IMessenger.Unregister, come illustrato nell'esempio di codice seguente:

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

Nota

In questo esempio non è completamente necessario chiamare Unregister perché WeakReferenceMessenger consentirà la Garbage Collection degli oggetti inutilizzati. Se si usasse StrongReferenceMessenger, sarebbe consigliabile chiamare Unregister per tutte le sottoscrizioni che non sono più in uso.

In questo esempio, la sintassi del metodo Unsubscribe specifica l'argomento di tipo del messaggio e l'oggetto destinatario in ascolto dei messaggi.

Riepilogo

L'interfaccia di MVVM Toolkit IMessenger descrive il modello di pubblicazione-sottoscrizione, consentendo la comunicazione basata su messaggi tra componenti che non è pratico collegare con riferimenti a oggetti e tipi. Questo meccanismo consente ai server di pubblicazione e ai sottoscrittori di comunicare senza avere un riferimento diretto l'uno all'altro, contribuendo a ridurre le dipendenze tra i componenti e consentendo lo sviluppo e il test indipendente di tali componenti.