// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; namespace CommunityToolkit.Mvvm.Messaging; /// /// An interface for a type providing the ability to exchange messages between different objects. /// This can be useful to decouple different modules of an application without having to keep strong /// references to types being referenced. It is also possible to send messages to specific channels, uniquely /// identified by a token, and to have different messengers in different sections of an applications. /// In order to use the functionalities, first define a message type, like so: /// /// public sealed class LoginCompletedMessage { } /// /// Then, register a recipient for this message: /// /// Messenger.Default.Register<MyRecipientType, LoginCompletedMessage>(this, (r, m) => /// { /// // Handle the message here... /// }); /// /// The message handler here is a lambda expression taking two parameters: the recipient and the message. /// This is done to avoid the allocations for the closures that would've been generated if the expression /// had captured the current instance. The recipient type parameter is used so that the recipient can be /// directly accessed within the handler without the need to manually perform type casts. This allows the /// code to be less verbose and more reliable, as all the checks are done just at build time. If the handler /// is defined within the same type as the recipient, it is also possible to directly access private members. /// This allows the message handler to be a static method, which enables the C# compiler to perform a number /// of additional memory optimizations (such as caching the delegate, avoiding unnecessary memory allocations). /// Finally, send a message when needed, like so: /// /// Messenger.Default.Send<LoginCompletedMessage>(); /// /// Additionally, the method group syntax can also be used to specify the message handler /// to invoke when receiving a message, if a method with the right signature is available /// in the current scope. This is helpful to keep the registration and handling logic separate. /// Following up from the previous example, consider a class having this method: /// /// private static void Receive(MyRecipientType recipient, LoginCompletedMessage message) /// { /// // Handle the message there /// } /// /// The registration can then be performed in a single line like so: /// /// Messenger.Default.Register(this, Receive); /// /// The C# compiler will automatically convert that expression to a instance /// compatible with . /// This will also work if multiple overloads of that method are available, each handling a different /// message type: the C# compiler will automatically pick the right one for the current message type. /// It is also possible to register message handlers explicitly using the interface. /// To do so, the recipient just needs to implement the interface and then call the /// extension, which will automatically register /// all the handlers that are declared by the recipient type. Registration for individual handlers is supported as well. /// public interface IMessenger { /// /// Checks whether or not a given recipient has already been registered for a message. /// /// The type of message to check for the given recipient. /// The type of token to check the channel for. /// The target recipient to check the registration for. /// The token used to identify the target channel to check. /// Whether or not has already been registered for the specified message. /// Thrown if or are . bool IsRegistered(object recipient, TToken token) where TMessage : class where TToken : IEquatable; /// /// Registers a recipient for a given type of message. /// /// The type of recipient for the message. /// The type of message to receive. /// The type of token to use to pick the messages to receive. /// The recipient that will receive the messages. /// A token used to determine the receiving channel to use. /// The to invoke when a message is received. /// Thrown if , or are . /// Thrown when trying to register the same message twice. void Register(TRecipient recipient, TToken token, MessageHandler handler) where TRecipient : class where TMessage : class where TToken : IEquatable; /// /// Unregisters a recipient from all registered messages. /// /// The recipient to unregister. /// /// This method will unregister the target recipient across all channels. /// Use this method as an easy way to lose all references to a target recipient. /// If the recipient has no registered handler, this method does nothing. /// /// Thrown if is . void UnregisterAll(object recipient); /// /// Unregisters a recipient from all messages on a specific channel. /// /// The type of token to identify what channel to unregister from. /// The recipient to unregister. /// The token to use to identify which handlers to unregister. /// If the recipient has no registered handler, this method does nothing. /// Thrown if or are . void UnregisterAll(object recipient, TToken token) where TToken : IEquatable; /// /// Unregisters a recipient from messages of a given type. /// /// The type of message to stop receiving. /// The type of token to identify what channel to unregister from. /// The recipient to unregister. /// The token to use to identify which handlers to unregister. /// If the recipient has no registered handler, this method does nothing. /// Thrown if or are . void Unregister(object recipient, TToken token) where TMessage : class where TToken : IEquatable; /// /// Sends a message of the specified type to all registered recipients. /// /// The type of message to send. /// The type of token to identify what channel to use to send the message. /// The message to send. /// The token indicating what channel to use. /// The message that was sent (ie. ). /// Thrown if or are . TMessage Send(TMessage message, TToken token) where TMessage : class where TToken : IEquatable; /// /// Performs a cleanup on the current messenger. /// Invoking this method does not unregister any of the currently registered /// recipient, and it can be used to perform cleanup operations such as /// trimming the internal data structures of a messenger implementation. /// void Cleanup(); /// /// Resets the instance and unregisters all the existing recipients. /// void Reset(); }