IMessengerExtensions.Observables.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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. // This file has two types which implement interfaces that can be projected to WInRT, ie.
  6. // 'Observable<TMessage>.Recipient' and 'Observable<TMessage, TToken>.Recipient', which
  7. // implement 'IDisposable' (which is projected to 'IClosable'). These types are not meant
  8. // to be used in interop scenarios (including in eg. bindings), as they're only meant to
  9. // be used by code behind interacting with System.Reactive APIs. As such, we skip marking
  10. // them partial, as we don't need CCW vtables to be generated for them.
  11. #pragma warning disable CsWinRT1028
  12. namespace CommunityToolkit.Mvvm.Messaging;
  13. /// <inheritdoc/>
  14. partial class IMessengerExtensions
  15. {
  16. /// <summary>
  17. /// Creates an <see cref="IObservable{T}"/> instance that can be used to be notified whenever a message of a given type is broadcast by a messenger.
  18. /// </summary>
  19. /// <typeparam name="TMessage">The type of message to use to receive notification for through the resulting <see cref="IObservable{T}"/> instance.</typeparam>
  20. /// <param name="messenger">The <see cref="IMessenger"/> instance to use to register the recipient.</param>
  21. /// <returns>An <see cref="IObservable{T}"/> instance to receive notifications for <typeparamref name="TMessage"/> messages being broadcast.</returns>
  22. /// <exception cref="System.ArgumentNullException">Thrown if <paramref name="messenger"/> is <see langword="null"/>.</exception>
  23. public static IObservable<TMessage> CreateObservable<TMessage>(this IMessenger messenger)
  24. where TMessage : class
  25. {
  26. ArgumentNullException.ThrowIfNull(messenger);
  27. return new Observable<TMessage>(messenger);
  28. }
  29. /// <summary>
  30. /// Creates an <see cref="IObservable{T}"/> instance that can be used to be notified whenever a message of a given type is broadcast by a messenger.
  31. /// </summary>
  32. /// <typeparam name="TMessage">The type of message to use to receive notification for through the resulting <see cref="IObservable{T}"/> instance.</typeparam>
  33. /// <typeparam name="TToken">The type of token to identify what channel to use to receive messages.</typeparam>
  34. /// <param name="messenger">The <see cref="IMessenger"/> instance to use to register the recipient.</param>
  35. /// <param name="token">A token used to determine the receiving channel to use.</param>
  36. /// <returns>An <see cref="IObservable{T}"/> instance to receive notifications for <typeparamref name="TMessage"/> messages being broadcast.</returns>
  37. /// <exception cref="System.ArgumentNullException">Thrown if <paramref name="messenger"/> or <paramref name="token"/> are <see langword="null"/>.</exception>
  38. public static IObservable<TMessage> CreateObservable<TMessage, TToken>(this IMessenger messenger, TToken token)
  39. where TMessage : class
  40. where TToken : IEquatable<TToken>
  41. {
  42. ArgumentNullException.ThrowIfNull(messenger);
  43. ArgumentNullException.For<TToken>.ThrowIfNull(token);
  44. return new Observable<TMessage, TToken>(messenger, token);
  45. }
  46. /// <summary>
  47. /// An <see cref="IObservable{T}"/> implementations for a given message type.
  48. /// </summary>
  49. /// <typeparam name="TMessage">The type of messages to listen to.</typeparam>
  50. private sealed class Observable<TMessage> : IObservable<TMessage>
  51. where TMessage : class
  52. {
  53. /// <summary>
  54. /// The <see cref="IMessenger"/> instance to use to register the recipient.
  55. /// </summary>
  56. private readonly IMessenger messenger;
  57. /// <summary>
  58. /// Creates a new <see cref="Observable{TMessage}"/> instance with the given parameters.
  59. /// </summary>
  60. /// <param name="messenger">The <see cref="IMessenger"/> instance to use to register the recipient.</param>
  61. public Observable(IMessenger messenger)
  62. {
  63. this.messenger = messenger;
  64. }
  65. /// <inheritdoc/>
  66. public IDisposable Subscribe(IObserver<TMessage> observer)
  67. {
  68. return new Recipient(this.messenger, observer);
  69. }
  70. /// <summary>
  71. /// An <see cref="IRecipient{TMessage}"/> implementation for <see cref="Observable{TMessage}"/>.
  72. /// </summary>
  73. private sealed class Recipient : IRecipient<TMessage>, IDisposable
  74. {
  75. /// <summary>
  76. /// The <see cref="IMessenger"/> instance to use to register the recipient.
  77. /// </summary>
  78. private readonly IMessenger messenger;
  79. /// <summary>
  80. /// The target <see cref="IObserver{T}"/> instance currently in use.
  81. /// </summary>
  82. private readonly IObserver<TMessage> observer;
  83. /// <summary>
  84. /// Creates a new <see cref="Recipient"/> instance with the specified parameters.
  85. /// </summary>
  86. /// <param name="messenger">The <see cref="IMessenger"/> instance to use to register the recipient.</param>
  87. /// <param name="observer">The <see cref="IObserver{T}"/> instance to use to create the recipient for.</param>
  88. public Recipient(IMessenger messenger, IObserver<TMessage> observer)
  89. {
  90. this.messenger = messenger;
  91. this.observer = observer;
  92. messenger.Register(this);
  93. }
  94. /// <inheritdoc/>
  95. public void Receive(TMessage message)
  96. {
  97. this.observer.OnNext(message);
  98. }
  99. /// <inheritdoc/>
  100. public void Dispose()
  101. {
  102. this.messenger.Unregister<TMessage>(this);
  103. }
  104. }
  105. }
  106. /// <summary>
  107. /// An <see cref="IObservable{T}"/> implementations for a given pair of message and token types.
  108. /// </summary>
  109. /// <typeparam name="TMessage">The type of messages to listen to.</typeparam>
  110. /// <typeparam name="TToken">The type of token to identify what channel to use to receive messages.</typeparam>
  111. private sealed class Observable<TMessage, TToken> : IObservable<TMessage>
  112. where TMessage : class
  113. where TToken : IEquatable<TToken>
  114. {
  115. /// <summary>
  116. /// The <see cref="IMessenger"/> instance to use to register the recipient.
  117. /// </summary>
  118. private readonly IMessenger messenger;
  119. /// <summary>
  120. /// The token used to determine the receiving channel to use.
  121. /// </summary>
  122. private readonly TToken token;
  123. /// <summary>
  124. /// Creates a new <see cref="Observable{TMessage, TToken}"/> instance with the given parameters.
  125. /// </summary>
  126. /// <param name="messenger">The <see cref="IMessenger"/> instance to use to register the recipient.</param>
  127. /// <param name="token">A token used to determine the receiving channel to use.</param>
  128. public Observable(IMessenger messenger, TToken token)
  129. {
  130. this.messenger = messenger;
  131. this.token = token;
  132. }
  133. /// <inheritdoc/>
  134. public IDisposable Subscribe(IObserver<TMessage> observer)
  135. {
  136. return new Recipient(this.messenger, observer, this.token);
  137. }
  138. /// <summary>
  139. /// An <see cref="IRecipient{TMessage}"/> implementation for <see cref="Observable{TMessage, TToken}"/>.
  140. /// </summary>
  141. private sealed class Recipient : IRecipient<TMessage>, IDisposable
  142. {
  143. /// <summary>
  144. /// The <see cref="IMessenger"/> instance to use to register the recipient.
  145. /// </summary>
  146. private readonly IMessenger messenger;
  147. /// <summary>
  148. /// The target <see cref="IObserver{T}"/> instance currently in use.
  149. /// </summary>
  150. private readonly IObserver<TMessage> observer;
  151. /// <summary>
  152. /// The token used to determine the receiving channel to use.
  153. /// </summary>
  154. private readonly TToken token;
  155. /// <summary>
  156. /// Creates a new <see cref="Recipient"/> instance with the specified parameters.
  157. /// </summary>
  158. /// <param name="messenger">The <see cref="IMessenger"/> instance to use to register the recipient.</param>
  159. /// <param name="observer">The <see cref="IObserver{T}"/> instance to use to create the recipient for.</param>
  160. /// <param name="token">A token used to determine the receiving channel to use.</param>
  161. public Recipient(IMessenger messenger, IObserver<TMessage> observer, TToken token)
  162. {
  163. this.messenger = messenger;
  164. this.observer = observer;
  165. this.token = token;
  166. messenger.Register(this, token);
  167. }
  168. /// <inheritdoc/>
  169. public void Receive(TMessage message)
  170. {
  171. this.observer.OnNext(message);
  172. }
  173. /// <inheritdoc/>
  174. public void Dispose()
  175. {
  176. this.messenger.Unregister<TMessage, TToken>(this, this.token);
  177. }
  178. }
  179. }
  180. }