IMessenger.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System;
  5. namespace CommunityToolkit.Mvvm.Messaging;
  6. /// <summary>
  7. /// An interface for a type providing the ability to exchange messages between different objects.
  8. /// This can be useful to decouple different modules of an application without having to keep strong
  9. /// references to types being referenced. It is also possible to send messages to specific channels, uniquely
  10. /// identified by a token, and to have different messengers in different sections of an applications.
  11. /// In order to use the <see cref="IMessenger"/> functionalities, first define a message type, like so:
  12. /// <code>
  13. /// public sealed class LoginCompletedMessage { }
  14. /// </code>
  15. /// Then, register a recipient for this message:
  16. /// <code>
  17. /// Messenger.Default.Register&lt;MyRecipientType, LoginCompletedMessage&gt;(this, (r, m) =>
  18. /// {
  19. /// // Handle the message here...
  20. /// });
  21. /// </code>
  22. /// The message handler here is a lambda expression taking two parameters: the recipient and the message.
  23. /// This is done to avoid the allocations for the closures that would've been generated if the expression
  24. /// had captured the current instance. The recipient type parameter is used so that the recipient can be
  25. /// directly accessed within the handler without the need to manually perform type casts. This allows the
  26. /// code to be less verbose and more reliable, as all the checks are done just at build time. If the handler
  27. /// is defined within the same type as the recipient, it is also possible to directly access private members.
  28. /// This allows the message handler to be a static method, which enables the C# compiler to perform a number
  29. /// of additional memory optimizations (such as caching the delegate, avoiding unnecessary memory allocations).
  30. /// Finally, send a message when needed, like so:
  31. /// <code>
  32. /// Messenger.Default.Send&lt;LoginCompletedMessage&gt;();
  33. /// </code>
  34. /// Additionally, the method group syntax can also be used to specify the message handler
  35. /// to invoke when receiving a message, if a method with the right signature is available
  36. /// in the current scope. This is helpful to keep the registration and handling logic separate.
  37. /// Following up from the previous example, consider a class having this method:
  38. /// <code>
  39. /// private static void Receive(MyRecipientType recipient, LoginCompletedMessage message)
  40. /// {
  41. /// // Handle the message there
  42. /// }
  43. /// </code>
  44. /// The registration can then be performed in a single line like so:
  45. /// <code>
  46. /// Messenger.Default.Register(this, Receive);
  47. /// </code>
  48. /// The C# compiler will automatically convert that expression to a <see cref="MessageHandler{TRecipient,TMessage}"/> instance
  49. /// compatible with <see cref="IMessengerExtensions.Register{TRecipient,TMessage}(IMessenger,TRecipient,MessageHandler{TRecipient,TMessage})"/>.
  50. /// This will also work if multiple overloads of that method are available, each handling a different
  51. /// message type: the C# compiler will automatically pick the right one for the current message type.
  52. /// It is also possible to register message handlers explicitly using the <see cref="IRecipient{TMessage}"/> interface.
  53. /// To do so, the recipient just needs to implement the interface and then call the
  54. /// <see cref="IMessengerExtensions.RegisterAll(IMessenger,object)"/> extension, which will automatically register
  55. /// all the handlers that are declared by the recipient type. Registration for individual handlers is supported as well.
  56. /// </summary>
  57. public interface IMessenger
  58. {
  59. /// <summary>
  60. /// Checks whether or not a given recipient has already been registered for a message.
  61. /// </summary>
  62. /// <typeparam name="TMessage">The type of message to check for the given recipient.</typeparam>
  63. /// <typeparam name="TToken">The type of token to check the channel for.</typeparam>
  64. /// <param name="recipient">The target recipient to check the registration for.</param>
  65. /// <param name="token">The token used to identify the target channel to check.</param>
  66. /// <returns>Whether or not <paramref name="recipient"/> has already been registered for the specified message.</returns>
  67. /// <exception cref="System.ArgumentNullException">Thrown if <paramref name="recipient"/> or <paramref name="token"/> are <see langword="null"/>.</exception>
  68. bool IsRegistered<TMessage, TToken>(object recipient, TToken token)
  69. where TMessage : class
  70. where TToken : IEquatable<TToken>;
  71. /// <summary>
  72. /// Registers a recipient for a given type of message.
  73. /// </summary>
  74. /// <typeparam name="TRecipient">The type of recipient for the message.</typeparam>
  75. /// <typeparam name="TMessage">The type of message to receive.</typeparam>
  76. /// <typeparam name="TToken">The type of token to use to pick the messages to receive.</typeparam>
  77. /// <param name="recipient">The recipient that will receive the messages.</param>
  78. /// <param name="token">A token used to determine the receiving channel to use.</param>
  79. /// <param name="handler">The <see cref="MessageHandler{TRecipient,TMessage}"/> to invoke when a message is received.</param>
  80. /// <exception cref="System.ArgumentNullException">Thrown if <paramref name="recipient"/>, <paramref name="token"/> or <paramref name="handler"/> are <see langword="null"/>.</exception>
  81. /// <exception cref="InvalidOperationException">Thrown when trying to register the same message twice.</exception>
  82. void Register<TRecipient, TMessage, TToken>(TRecipient recipient, TToken token, MessageHandler<TRecipient, TMessage> handler)
  83. where TRecipient : class
  84. where TMessage : class
  85. where TToken : IEquatable<TToken>;
  86. /// <summary>
  87. /// Unregisters a recipient from all registered messages.
  88. /// </summary>
  89. /// <param name="recipient">The recipient to unregister.</param>
  90. /// <remarks>
  91. /// This method will unregister the target recipient across all channels.
  92. /// Use this method as an easy way to lose all references to a target recipient.
  93. /// If the recipient has no registered handler, this method does nothing.
  94. /// </remarks>
  95. /// <exception cref="System.ArgumentNullException">Thrown if <paramref name="recipient"/> is <see langword="null"/>.</exception>
  96. void UnregisterAll(object recipient);
  97. /// <summary>
  98. /// Unregisters a recipient from all messages on a specific channel.
  99. /// </summary>
  100. /// <typeparam name="TToken">The type of token to identify what channel to unregister from.</typeparam>
  101. /// <param name="recipient">The recipient to unregister.</param>
  102. /// <param name="token">The token to use to identify which handlers to unregister.</param>
  103. /// <remarks>If the recipient has no registered handler, this method does nothing.</remarks>
  104. /// <exception cref="System.ArgumentNullException">Thrown if <paramref name="recipient"/> or <paramref name="token"/> are <see langword="null"/>.</exception>
  105. void UnregisterAll<TToken>(object recipient, TToken token)
  106. where TToken : IEquatable<TToken>;
  107. /// <summary>
  108. /// Unregisters a recipient from messages of a given type.
  109. /// </summary>
  110. /// <typeparam name="TMessage">The type of message to stop receiving.</typeparam>
  111. /// <typeparam name="TToken">The type of token to identify what channel to unregister from.</typeparam>
  112. /// <param name="recipient">The recipient to unregister.</param>
  113. /// <param name="token">The token to use to identify which handlers to unregister.</param>
  114. /// <remarks>If the recipient has no registered handler, this method does nothing.</remarks>
  115. /// <exception cref="System.ArgumentNullException">Thrown if <paramref name="recipient"/> or <paramref name="token"/> are <see langword="null"/>.</exception>
  116. void Unregister<TMessage, TToken>(object recipient, TToken token)
  117. where TMessage : class
  118. where TToken : IEquatable<TToken>;
  119. /// <summary>
  120. /// Sends a message of the specified type to all registered recipients.
  121. /// </summary>
  122. /// <typeparam name="TMessage">The type of message to send.</typeparam>
  123. /// <typeparam name="TToken">The type of token to identify what channel to use to send the message.</typeparam>
  124. /// <param name="message">The message to send.</param>
  125. /// <param name="token">The token indicating what channel to use.</param>
  126. /// <returns>The message that was sent (ie. <paramref name="message"/>).</returns>
  127. /// <exception cref="System.ArgumentNullException">Thrown if <paramref name="message"/> or <paramref name="token"/> are <see langword="null"/>.</exception>
  128. TMessage Send<TMessage, TToken>(TMessage message, TToken token)
  129. where TMessage : class
  130. where TToken : IEquatable<TToken>;
  131. /// <summary>
  132. /// Performs a cleanup on the current messenger.
  133. /// Invoking this method does not unregister any of the currently registered
  134. /// recipient, and it can be used to perform cleanup operations such as
  135. /// trimming the internal data structures of a messenger implementation.
  136. /// </summary>
  137. void Cleanup();
  138. /// <summary>
  139. /// Resets the <see cref="IMessenger"/> instance and unregisters all the existing recipients.
  140. /// </summary>
  141. void Reset();
  142. }