/// <summary>
        /// Raises an event safely, ensuring that all handlers are called on the proper thread, and any exceptions do not prevent other handlers being called.
        /// </summary>
        /// <param name="handler">The event to raise.</param>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">Event arguments for the event.</param>
        /// <exception cref="AggregateException">Thrown if any handlers raise exceptions, with the exceptions raised captured in the <see cref="AggregateException.InnerExceptions"/> property.</exception>
        /// <remarks>Temporarily made internal, as the use cases for this class really aren't clear enough to be confident of a suitable implementation of ISynchronizedObject.</remarks>
        public static void SafeRaise(this Delegate handler, ISynchronizedObject sender, EventArgs e)
        {
            sender.ThrowIfNull(nameof(sender));
            if (handler is null)
            {
                return;
            }

            if (handler.Method.GetParameters() is ParameterInfo[] handlerParams && (handlerParams.Length != 2 ||
                                                                                    !handlerParams[0].ParameterType.IsAssignableFrom(sender.GetType()) ||
                                                                                    !handlerParams[1].ParameterType.IsAssignableFrom(e?.GetType())))
            {
                throw new ArgumentException("Sender and event args must match handler parameter types.");
            }

            List <Exception> raisedExceptions = null;

            sender.SynchronizationContext.Post(
                state =>
            {
                foreach (Delegate del in handler.GetInvocationList())
                {
                    try
                    {
                        del.DynamicInvoke(sender, e);
                    }
                    catch (TargetInvocationException ex) when(ex.InnerException is Exception)
                    {
                        if (raisedExceptions is null)
                        {
                            raisedExceptions = new List <Exception>();
                        }

                        raisedExceptions.Add(ex.InnerException);
                    }
                }
            }, null);

            // Check list of exceptions is either still null, or not empty.
            Debug.Assert(raisedExceptions is null || raisedExceptions.Any(), "Empty list of exceptions after handling event.");
            if (raisedExceptions is List <Exception> )
            {
                throw new AggregateException(Properties.Resources.SafeRaiseExceptionMessage, raisedExceptions);
            }
        }
        /// <summary>
        /// Raises an event safely, ensuring that all handlers are called on the proper thread, and any exceptions do not prevent other handlers being called.
        /// </summary>
        /// <param name="handler">The event to raise.</param>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">Event arguments for the event.</param>
        /// <exception cref="AggregateException">Thrown if any handlers raise exceptions, with the exceptions raised captured in the <see cref="AggregateException.InnerExceptions"/> property.</exception>
        /// <remarks>Temporarily made internal, as the use cases for this class really aren't clear enough to be confident of a suitable implementation of ISynchronizedObject.</remarks>
        public static void SafeRaise(this EventHandler handler, ISynchronizedObject sender, EventArgs e)
        {
            sender.ThrowIfNull(nameof(sender));
            if (handler is null)
            {
                return;
            }

            List <Exception> raisedExceptions = null;

            sender.SynchronizationContext.Post(
                state =>
            {
                foreach (Delegate del in handler.GetInvocationList())
                {
                    try
                    {
                        del.DynamicInvoke(sender, e);
                    }
                    catch (TargetInvocationException ex) when(ex.InnerException is Exception)
                    {
                        if (raisedExceptions is null)
                        {
                            raisedExceptions = new List <Exception>();
                        }

                        raisedExceptions.Add(ex.InnerException);
                    }
                }
            }, null);

            // Check list of exceptions is either still null, or not empty.
            Debug.Assert(raisedExceptions is null || raisedExceptions.Any(), "Empty list of exceptions after handling event.");
            if (raisedExceptions is List <Exception> )
            {
                throw new AggregateException(Properties.Resources.SafeRaiseExceptionMessage, raisedExceptions);
            }
        }