< Summary

Information
Class: MyNet.Messaging.Messenger
Assembly: MyNet.Messaging
File(s): https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.Messaging/Messenger.cs
Tag: 323_28699572109
Line coverage
97%
Covered lines: 133
Uncovered lines: 4
Coverable lines: 137
Total lines: 594
Line coverage: 97%
Branch coverage
91%
Covered branches: 90
Total branches: 98
Branch coverage: 91.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor()100%11100%
get_Default()50%4225%
OverrideDefault(...)100%11100%
Reset()100%11100%
Register(...)100%11100%
Register(...)100%11100%
Register(...)100%88100%
Register(...)100%11100%
Send(...)100%11100%
Send()100%11100%
Send(...)100%11100%
Unregister(...)100%11100%
Unregister(...)100%11100%
Unregister(...)100%11100%
Unregister(...)100%11100%
Cleanup()100%11100%
CleanupList(...)100%1010100%
SendToList(...)91.66%2424100%
UnregisterFromLists(...)90%1010100%
UnregisterFromLists(...)90.9%2222100%
SendToTargetOrType(...)94.44%1818100%
Dispose()100%11100%
Dispose(...)75%4487.5%
Finalize()100%11100%

File(s)

https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.Messaging/Messenger.cs

#LineLine coverage
 1// -----------------------------------------------------------------------
 2// <copyright file="Messenger.cs" company="Stéphane ANDRE">
 3// Copyright (c) Stéphane ANDRE. All rights reserved.
 4// </copyright>
 5// -----------------------------------------------------------------------
 6
 7using System;
 8using System.Collections.Generic;
 9using System.Linq;
 10using System.Threading;
 11
 12namespace MyNet.Messaging;
 13
 14/// <summary>
 15/// The Messenger is a class allowing objects to exchange messages.
 16/// </summary>
 17public class Messenger : IMessenger, IDisposable
 18{
 319    private static readonly Lock CreationLock = new();
 8720    private readonly ReaderWriterLockSlim _recipientsOfSubclassesActionLock = new();
 8721    private readonly ReaderWriterLockSlim _recipientsStrictActionLock = new();
 8722    private readonly ReaderWriterLockSlim _registerLock = new();
 23    private Dictionary<Type, List<WeakActionAndToken>>? _recipientsOfSubclassesAction;
 24    private Dictionary<Type, List<WeakActionAndToken>>? _recipientsStrictAction;
 25    private bool _disposed;
 26
 27    /// <summary>
 28    /// Gets the Messenger's default instance, allowing
 29    /// to register and send messages in a static manner.
 30    /// </summary>
 31    public static IMessenger? Default
 32    {
 33        get
 34        {
 1235            if (field != null) return field;
 36            lock (CreationLock)
 37            {
 038                field = new Messenger();
 039            }
 40
 041            return field;
 42        }
 43
 44        private set;
 45    }
 46
 47    /// <summary>
 48    /// Provides a way to override the Messenger.Current instance with
 49    /// a custom instance, for example for unit testing purposes.
 50    /// </summary>
 51    /// <param name="newMessenger">The instance that will be used as Messenger.Current.</param>
 352    public static void OverrideDefault(IMessenger newMessenger) => Default = newMessenger;
 53
 54    /// <summary>
 55    /// Sets the Messenger's default (static) instance to null.
 56    /// </summary>
 657    public static void Reset() => Default = null;
 58
 59    #region IMessenger Members
 60
 61    /// <summary>
 62    /// Registers a recipient for a type of message TMessage. The action
 63    /// parameter will be executed when a corresponding message is sent.
 64    /// <para>Registering a recipient does not create a hard reference to it,
 65    /// so if this recipient is deleted, no memory leak is caused.</para>
 66    /// </summary>
 67    /// <typeparam name="TMessage">The type of message that the recipient registers
 68    /// for.</typeparam>
 69    /// <param name="recipient">The recipient that will receive the messages.</param>
 70    /// <param name="action">The action that will be executed when a message
 71    /// of type TMessage is sent. IMPORTANT: If the action causes a closure,
 72    /// you must set keepTargetAlive to true to avoid side effects. </param>
 73    /// <param name="keepTargetAlive">If true, the target of the Action will
 74    /// be kept as a hard reference, which might cause a memory leak. You should only set this
 75    /// parameter to true if the action is using closures.</param>
 76    public virtual void Register<TMessage>(
 77        object recipient,
 78        Action<TMessage> action,
 8479        bool keepTargetAlive = false) => Register(recipient, null, false, action, keepTargetAlive);
 80
 81    /// <summary>
 82    /// Registers a recipient for a type of message TMessage.
 83    /// The action parameter will be executed when a corresponding
 84    /// message is sent. See the receiveDerivedMessagesToo parameter
 85    /// for details on how messages deriving from TMessage (or, if TMessage is an interface,
 86    /// messages implementing TMessage) can be received too.
 87    /// <para>Registering a recipient does not create a hard reference to it,
 88    /// so if this recipient is deleted, no memory leak is caused.</para>
 89    /// <para>However, if you use closures and set keepTargetAlive to true, you might
 90    /// cause a memory leak if you don't call <see cref="Unregister"/> when you are cleaning up.</para>
 91    /// </summary>
 92    /// <typeparam name="TMessage">The type of message that the recipient registers
 93    /// for.</typeparam>
 94    /// <param name="recipient">The recipient that will receive the messages.</param>
 95    /// <param name="token">A token for a messaging channel. If a recipient registers
 96    /// using a token, and a sender sends a message using the same token, then this
 97    /// message will be delivered to the recipient. Other recipients who did not
 98    /// use a token when registering (or who used a different token) will not
 99    /// get the message. Similarly, messages sent without any token, or with a different
 100    /// token, will not be delivered to that recipient.</param>
 101    /// <param name="action">The action that will be executed when a message
 102    /// of type TMessage is sent. IMPORTANT: If the action causes a closure,
 103    /// you must set keepTargetAlive to true to avoid side effects. </param>
 104    /// <param name="keepTargetAlive">If true, the target of the Action will
 105    /// be kept as a hard reference, which might cause a memory leak. You should only set this
 106    /// parameter to true if the action is using closures.</param>
 107    public virtual void Register<TMessage>(
 108        object recipient,
 109        object token,
 110        Action<TMessage> action,
 24111        bool keepTargetAlive = false) => Register(recipient, token, false, action, keepTargetAlive);
 112
 113    /// <summary>
 114    /// Registers a recipient for a type of message TMessage.
 115    /// The action parameter will be executed when a corresponding
 116    /// message is sent. See the receiveDerivedMessagesToo parameter
 117    /// for details on how messages deriving from TMessage (or, if TMessage is an interface,
 118    /// messages implementing TMessage) can be received too.
 119    /// <para>Registering a recipient does not create a hard reference to it,
 120    /// so if this recipient is deleted, no memory leak is caused.</para>
 121    /// </summary>
 122    /// <typeparam name="TMessage">The type of message that the recipient registers
 123    /// for.</typeparam>
 124    /// <param name="recipient">The recipient that will receive the messages.</param>
 125    /// <param name="token">A token for a messaging channel. If a recipient registers
 126    /// using a token, and a sender sends a message using the same token, then this
 127    /// message will be delivered to the recipient. Other recipients who did not
 128    /// use a token when registering (or who used a different token) will not
 129    /// get the message. Similarly, messages sent without any token, or with a different
 130    /// token, will not be delivered to that recipient.</param>
 131    /// <param name="receiveDerivedMessagesToo">If true, message types deriving from
 132    /// TMessage will also be transmitted to the recipient. For example, if a SendOrderMessage
 133    /// and an ExecuteOrderMessage derive from OrderMessage, registering for OrderMessage
 134    /// and setting receiveDerivedMessagesToo to true will send SendOrderMessage
 135    /// and ExecuteOrderMessage to the recipient that registered.
 136    /// <para>Also, if TMessage is an interface, message types implementing TMessage will also be
 137    /// transmitted to the recipient. For example, if a SendOrderMessage
 138    /// and an ExecuteOrderMessage implement IOrderMessage, registering for IOrderMessage
 139    /// and setting receiveDerivedMessagesToo to true will send SendOrderMessage
 140    /// and ExecuteOrderMessage to the recipient that registered.</para>
 141    /// </param>
 142    /// <param name="action">The action that will be executed when a message
 143    /// of type TMessage is sent. IMPORTANT: If the action causes a closure,
 144    /// you must set keepTargetAlive to true to avoid side effects. </param>
 145    /// <param name="keepTargetAlive">If true, the target of the Action will
 146    /// be kept as a hard reference, which might cause a memory leak. You should only set this
 147    /// parameter to true if the action is using closures.</param>
 148    public virtual void Register<TMessage>(
 149        object? recipient,
 150        object? token,
 151        bool receiveDerivedMessagesToo,
 152        Action<TMessage> action,
 153        bool keepTargetAlive = false)
 154    {
 120155        _registerLock.EnterWriteLock();
 156        try
 157        {
 120158            var messageType = typeof(TMessage);
 159
 160            Dictionary<Type, List<WeakActionAndToken>> recipients;
 161
 120162            if (receiveDerivedMessagesToo)
 163            {
 6164                _recipientsOfSubclassesAction ??= [];
 165
 6166                recipients = _recipientsOfSubclassesAction;
 167            }
 168            else
 169            {
 114170                _recipientsStrictAction ??= [];
 171
 114172                recipients = _recipientsStrictAction;
 173            }
 174
 120175            lock (recipients)
 176            {
 177                List<WeakActionAndToken> list;
 178
 120179                if (!recipients.TryGetValue(messageType, out var value))
 180                {
 87181                    list = [];
 87182                    recipients.Add(messageType, list);
 183                }
 184                else
 185                {
 33186                    list = value;
 187                }
 188
 189#pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of referen
 120190                var weakAction = new WeakAction<TMessage>(recipient, action, keepTargetAlive);
 191#pragma warning restore CS8620 // Argument cannot be used for parameter due to differences in the nullability of referen
 192
 120193                var item = new WeakActionAndToken { Action = weakAction, Token = token };
 194
 120195                list.Add(item);
 120196            }
 197        }
 198        finally
 199        {
 120200            _registerLock.ExitWriteLock();
 120201        }
 202
 120203        Cleanup();
 120204    }
 205
 206    /// <summary>
 207    /// Registers a recipient for a type of message TMessage.
 208    /// The action parameter will be executed when a corresponding
 209    /// message is sent. See the receiveDerivedMessagesToo parameter
 210    /// for details on how messages deriving from TMessage (or, if TMessage is an interface,
 211    /// messages implementing TMessage) can be received too.
 212    /// <para>Registering a recipient does not create a hard reference to it,
 213    /// so if this recipient is deleted, no memory leak is caused.</para>
 214    /// </summary>
 215    /// <typeparam name="TMessage">The type of message that the recipient registers
 216    /// for.</typeparam>
 217    /// <param name="recipient">The recipient that will receive the messages.</param>
 218    /// <param name="receiveDerivedMessagesToo">If true, message types deriving from
 219    /// TMessage will also be transmitted to the recipient. For example, if a SendOrderMessage
 220    /// and an ExecuteOrderMessage derive from OrderMessage, registering for OrderMessage
 221    /// and setting receiveDerivedMessagesToo to true will send SendOrderMessage
 222    /// and ExecuteOrderMessage to the recipient that registered.
 223    /// <para>Also, if TMessage is an interface, message types implementing TMessage will also be
 224    /// transmitted to the recipient. For example, if a SendOrderMessage
 225    /// and an ExecuteOrderMessage implement IOrderMessage, registering for IOrderMessage
 226    /// and setting receiveDerivedMessagesToo to true will send SendOrderMessage
 227    /// and ExecuteOrderMessage to the recipient that registered.</para>
 228    /// </param>
 229    /// <param name="action">The action that will be executed when a message
 230    /// of type TMessage is sent. IMPORTANT: If the action causes a closure,
 231    /// you must set keepTargetAlive to true to avoid side effects. </param>
 232    /// <param name="keepTargetAlive">If true, the target of the Action will
 233    /// be kept as a hard reference, which might cause a memory leak. You should only set this
 234    /// parameter to true if the action is using closures.</param>
 235    public virtual void Register<TMessage>(
 236        object recipient,
 237        bool receiveDerivedMessagesToo,
 238        Action<TMessage> action,
 12239        bool keepTargetAlive = false) => Register(recipient, null, receiveDerivedMessagesToo, action, keepTargetAlive);
 240
 241    /// <summary>
 242    /// Sends a message to registered recipients. The message will
 243    /// reach all recipients that registered for this message type
 244    /// using one of the Register methods.
 245    /// </summary>
 246    /// <typeparam name="TMessage">The type of message that will be sent.</typeparam>
 247    /// <param name="message">The message to send to registered recipients.</param>
 72248    public virtual void Send<TMessage>(TMessage message) => SendToTargetOrType(message, null, null);
 249
 3250    public virtual void Send<TMessage>() => SendToTargetOrType(Activator.CreateInstance<TMessage>(), null, null);
 251
 252    /// <summary>
 253    /// Sends a message to registered recipients. The message will
 254    /// reach only recipients that registered for this message type
 255    /// using one of the Register methods, and that are
 256    /// of the targetType.
 257    /// </summary>
 258    /// <typeparam name="TMessage">The type of message that will be sent.</typeparam>
 259    /// <typeparam name="TTarget">The type of recipients that will receive
 260    /// the message. The message won't be sent to recipients of another type.</typeparam>
 261    /// <param name="message">The message to send to registered recipients.</param>
 6262    public virtual void Send<TMessage, TTarget>(TMessage message) => SendToTargetOrType(message, typeof(TTarget), null);
 263
 264    /// <summary>
 265    /// Sends a message to registered recipients. The message will
 266    /// reach only recipients that registered for this message type
 267    /// using one of the Register methods, and that are
 268    /// of the targetType.
 269    /// </summary>
 270    /// <typeparam name="TMessage">The type of message that will be sent.</typeparam>
 271    /// <param name="message">The message to send to registered recipients.</param>
 272    /// <param name="token">A token for a messaging channel. If a recipient registers
 273    /// using a token, and a sender sends a message using the same token, then this
 274    /// message will be delivered to the recipient. Other recipients who did not
 275    /// use a token when registering (or who used a different token) will not
 276    /// get the message. Similarly, messages sent without any token, or with a different
 277    /// token, will not be delivered to that recipient.</param>
 18278    public virtual void Send<TMessage>(TMessage message, object token) => SendToTargetOrType(message, null, token);
 279
 280    /// <summary>
 281    /// Unregisters a message recipient completely. After this method
 282    /// is executed, the recipient will not receive any messages anymore.
 283    /// </summary>
 284    /// <param name="recipient">The recipient that must be unregistered.</param>
 285    public virtual void Unregister(object? recipient)
 286    {
 6287        UnregisterFromLists(recipient, _recipientsOfSubclassesAction);
 6288        UnregisterFromLists(recipient, _recipientsStrictAction);
 6289    }
 290
 291    /// <summary>
 292    /// Unregisters a message recipient for a given type of messages only.
 293    /// After this method is executed, the recipient will not receive messages
 294    /// of type TMessage anymore, but will still receive other message types (if it
 295    /// registered for them previously).
 296    /// </summary>
 297    /// <param name="recipient">The recipient that must be unregistered.</param>
 298    /// <typeparam name="TMessage">The type of messages that the recipient wants
 299    /// to unregister from.</typeparam>
 3300    public virtual void Unregister<TMessage>(object? recipient) => Unregister<TMessage>(recipient, null, null);
 301
 302    /// <summary>
 303    /// Unregisters a message recipient for a given type of messages only and for a given token.
 304    /// After this method is executed, the recipient will not receive messages
 305    /// of type TMessage anymore with the given token, but will still receive other message types
 306    /// or messages with other tokens (if it registered for them previously).
 307    /// </summary>
 308    /// <param name="recipient">The recipient that must be unregistered.</param>
 309    /// <param name="token">The token for which the recipient must be unregistered.</param>
 310    /// <typeparam name="TMessage">The type of messages that the recipient wants
 311    /// to unregister from.</typeparam>
 6312    public virtual void Unregister<TMessage>(object? recipient, object? token) => Unregister<TMessage>(recipient, token,
 313
 314    /// <summary>
 315    /// Unregisters a message recipient for a given type of messages and for
 316    /// a given action. Other message types will still be transmitted to the
 317    /// recipient (if it registered for them previously). Other actions that have
 318    /// been registered for the message type TMessage and for the given recipient (if
 319    /// available) will also remain available.
 320    /// </summary>
 321    /// <typeparam name="TMessage">The type of messages that the recipient wants
 322    /// to unregister from.</typeparam>
 323    /// <param name="recipient">The recipient that must be unregistered.</param>
 324    /// <param name="action">The action that must be unregistered for
 325    /// the recipient and for the message type TMessage.</param>
 9326    public virtual void Unregister<TMessage>(object? recipient, Action<TMessage>? action) => Unregister(recipient, null,
 327
 328    /// <summary>
 329    /// Unregisters a message recipient for a given type of messages, for
 330    /// a given action and a given token. Other message types will still be transmitted to the
 331    /// recipient (if it registered for them previously). Other actions that have
 332    /// been registered for the message type TMessage, for the given recipient and other tokens (if
 333    /// available) will also remain available.
 334    /// </summary>
 335    /// <typeparam name="TMessage">The type of messages that the recipient wants
 336    /// to unregister from.</typeparam>
 337    /// <param name="recipient">The recipient that must be unregistered.</param>
 338    /// <param name="token">The token for which the recipient must be unregistered.</param>
 339    /// <param name="action">The action that must be unregistered for
 340    /// the recipient and for the message type TMessage.</param>
 341    public virtual void Unregister<TMessage>(object? recipient, object? token, Action<TMessage>? action)
 342    {
 18343        UnregisterFromLists(recipient, token, action, _recipientsStrictAction);
 18344        UnregisterFromLists(recipient, token, action, _recipientsOfSubclassesAction);
 18345        Cleanup();
 18346    }
 347
 348    #endregion
 349
 350    /// <summary>
 351    /// Scans the recipients' lists for "dead" instances and removes them.
 352    /// Since recipients are stored as <see cref="WeakReference"/>,
 353    /// recipients can be garbage collected even though the Messenger keeps
 354    /// them in a list. During the cleanup operation, all "dead"
 355    /// recipients are removed from the lists. Since this operation
 356    /// can take a moment, it is only executed when the application is
 357    /// idle. For this reason, a user of the Messenger class should use
 358    /// RequestCleanup instead of forcing one with the
 359    /// <see cref="Cleanup" /> method.
 360    /// </summary>
 361    public void Cleanup()
 362    {
 240363        CleanupList(_recipientsOfSubclassesAction);
 240364        CleanupList(_recipientsStrictAction);
 240365    }
 366
 367    private static void CleanupList(IDictionary<Type, List<WeakActionAndToken>>? lists)
 368    {
 480369        if (lists == null)
 370        {
 234371            return;
 372        }
 373
 246374        lock (lists)
 375        {
 246376            var listsToRemove = new List<Type>();
 972377            foreach (var list in lists)
 378            {
 240379                var recipientsToRemove = list.Value
 240380                    .Where(item => item.Action is not { IsAlive: true })
 240381                    .ToList();
 382
 534383                foreach (var recipient in recipientsToRemove)
 384                {
 27385                    _ = list.Value.Remove(recipient);
 386                }
 387
 240388                if (list.Value.Count == 0)
 389                {
 21390                    listsToRemove.Add(list.Key);
 391                }
 392            }
 393
 534394            foreach (var key in listsToRemove)
 395            {
 21396                _ = lists.Remove(key);
 397            }
 398        }
 246399    }
 400
 401    private static void SendToList<TMessage>(
 402        TMessage? message,
 403        IEnumerable<WeakActionAndToken>? weakActionsAndTokens,
 404        Type? messageTargetType,
 405        object? token)
 406    {
 84407        if (weakActionsAndTokens == null) return;
 408
 409        // Clone to protect from people registering in a "receive message" method
 410        // Correction Messaging BL0004.007
 84411        var listClone = new List<WeakActionAndToken>(weakActionsAndTokens);
 412
 390413        foreach (var item in listClone)
 414        {
 111415            if (item.Action is { IsAlive: true, Target: not null } executeAction
 111416                && (messageTargetType == null
 111417                    || item.Action.Target.GetType() == messageTargetType
 111418                    || messageTargetType.IsInstanceOfType(item.Action.Target))
 111419                && ((item.Token == null && token == null)
 111420                    || (item.Token?.Equals(token) == true)))
 421            {
 90422                executeAction.ExecuteWithObject(message);
 423            }
 424        }
 84425    }
 426
 427    private static void UnregisterFromLists(object? recipient, Dictionary<Type, List<WeakActionAndToken>>? lists)
 428    {
 12429        if (recipient == null
 12430            || lists is not { Count: not 0 })
 431        {
 6432            return;
 433        }
 434
 6435        lock (lists)
 436        {
 30437            foreach (var weakAction in lists.Keys.SelectMany(messageType => lists[messageType].Select(item => item.Actio
 438            {
 9439                weakAction.MarkForDeletion();
 440            }
 441        }
 6442    }
 443
 444    private static void UnregisterFromLists<TMessage>(
 445        object? recipient,
 446        object? token,
 447        Action<TMessage>? action,
 448        Dictionary<Type, List<WeakActionAndToken>>? lists)
 449    {
 36450        var messageType = typeof(TMessage);
 451
 36452        if (recipient == null
 36453            || lists is not { Count: not 0 }
 36454            || !lists.TryGetValue(messageType, out var value))
 455        {
 18456            return;
 457        }
 458
 18459        lock (lists)
 460        {
 84461            foreach (var item in value)
 462            {
 24463                if (item.Action is WeakAction<TMessage> weakActionCasted
 24464                    && recipient == weakActionCasted.Target
 24465                    && (action == null
 24466                        || action.Method.Name == weakActionCasted.MethodName)
 24467                    && (token?.Equals(item.Token) != false))
 468                {
 18469                    item.Action.MarkForDeletion();
 470                }
 471            }
 472        }
 18473    }
 474
 475    private void SendToTargetOrType<TMessage>(TMessage? message, Type? messageTargetType, object? token)
 476    {
 99477        var messageType = typeof(TMessage);
 478
 99479        if (_recipientsOfSubclassesAction != null)
 480        {
 481            // Clone to protect from people registering in a "receive message" method
 482            // Correction Messaging BL0008.002
 6483            _recipientsOfSubclassesActionLock.EnterReadLock();
 484            try
 485            {
 6486                var listClone = new List<Type>(_recipientsOfSubclassesAction.Keys);
 487
 24488                foreach (var type in listClone)
 489                {
 6490                    List<WeakActionAndToken>? list = null;
 491
 6492                    if (messageType == type
 6493                        || messageType.IsSubclassOf(type)
 6494                        || type.IsAssignableFrom(messageType))
 495                    {
 6496                        lock (_recipientsOfSubclassesAction)
 497                        {
 6498                            if (_recipientsOfSubclassesAction.TryGetValue(type, out var value))
 499                            {
 6500                                list = [.. value];
 501                            }
 6502                        }
 503                    }
 504
 6505                    SendToList(message, list, messageTargetType, token);
 506                }
 507            }
 508            finally
 509            {
 6510                _recipientsOfSubclassesActionLock.ExitReadLock();
 6511            }
 512        }
 513
 99514        if (_recipientsStrictAction != null)
 515        {
 96516            List<WeakActionAndToken>? list = null;
 517
 96518            _recipientsStrictActionLock.EnterReadLock();
 519            try
 520            {
 96521                if (_recipientsStrictAction.TryGetValue(messageType, out var value))
 522                {
 78523                    list = [.. value];
 524                }
 96525            }
 526            finally
 527            {
 96528                _recipientsStrictActionLock.ExitReadLock();
 96529            }
 530
 96531            if (list != null)
 532            {
 78533                SendToList(message, list, messageTargetType, token);
 534            }
 535        }
 536
 99537        Cleanup();
 99538    }
 539
 540    #region Nested type: WeakActionAndToken
 541
 542    private readonly record struct WeakActionAndToken
 543    {
 544#pragma warning disable CA1859 // Use concrete types instead of base types when possible for better performance
 545        /// <summary>
 546        /// Gets stores a WeakAction implementation. Using interface for polymorphism while CA1859 is disabled
 547        /// because the actual type depends on the message type stored in the dictionary key.
 548        /// </summary>
 549        public IExecuteWithObject? Action { get; init; }
 550#pragma warning restore CA1859
 551
 552        /// <summary>
 553        /// Gets stores the token associated with the action. This token is used to filter messages when sending, ensuri
 554        /// </summary>
 555        public object? Token { get; init; }
 556    }
 557
 558    #endregion
 559
 560    /// <summary>
 561    /// Releases all resources used by the Messenger.
 562    /// </summary>
 563    public void Dispose()
 564    {
 84565        Dispose(true);
 84566        GC.SuppressFinalize(this);
 84567    }
 568
 569    /// <summary>
 570    /// Releases the unmanaged resources used by the Messenger and optionally releases managed resources.
 571    /// </summary>
 572    /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged re
 573    protected virtual void Dispose(bool disposing)
 574    {
 87575        if (_disposed)
 0576            return;
 577
 87578        if (disposing)
 579        {
 84580            _registerLock.Dispose();
 84581            _recipientsOfSubclassesActionLock.Dispose();
 84582            _recipientsStrictActionLock.Dispose();
 583        }
 584
 87585        _disposed = true;
 87586    }
 587
 588    /// <summary>
 589    /// Finalizes an instance of the <see cref="Messenger"/> class.
 590    /// Finalizer ensures cleanup if Dispose is not called.
 591    /// </summary>
 3592    ~Messenger() => Dispose(false);
 593}
 594