/// <summary>
        ///     Invokes the delegate given (if not <see langword="null" />) with protection against exceptions thrown by
        ///     the invoked methods.
        /// </summary>
        /// <typeparam name="T">
        ///     The type of the event args to pass.
        /// </typeparam>
        /// <param name="handler">
        ///     The delegate to call.
        /// </param>
        /// <param name="eventName">
        ///     The name of the event being raised.
        /// </param>
        /// <param name="sender">
        ///     The object to pass as the sender.
        /// </param>
        /// <param name="eventArgFactory">
        ///     A factory for generating event args.
        /// </param>
        /// <param name="usage">
        ///     The way in which to use the factory to generate args.
        /// </param>
        /// <returns>
        ///     <see langword="true" /> if at least one delegate was successfully invoked, <see langword="false" /> otherwise.
        /// </returns>
        /// <exception cref="ArgumentOutOfRangeException">
        ///     Invalid value passed in the <paramref name="usage" /> argument.
        /// </exception>
        public static bool SafeInvoke <T>([CanBeNull] this Delegate handler, [NotNull] string eventName, [CanBeNull] object sender,
                                          EventArgsFactory <T> eventArgFactory, ArgsUsageKind usage) where T : EventArgs
        {
            eventName.Validate(nameof(eventName), StringIs.NotNullEmptyOrWhiteSpace);
            eventArgFactory.Validate(nameof(eventArgFactory), ObjectIs.NotNull);
            if (ReferenceEquals(handler, null))
            {
                return(false);
            }

            var delegates = handler.GetInvocationList();

            object[]? args = null;
            bool generateArgs;

            switch (usage)
            {
            case ArgsUsageKind.Reuse:
                args         = new[] { sender, eventArgFactory() };
                generateArgs = false;
                break;

            case ArgsUsageKind.UniqueInstance:
                generateArgs = true;
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(usage), usage, string.Format(Resources.Exceptions.DelegateExtensions_InvalidUsage, nameof(usage), System.Globalization.CultureInfo.CurrentCulture));
            }

            var raised = false;

            foreach (var callback in delegates)
            {
                try
                {
                    if (generateArgs)
                    {
                        args = new[] { sender, eventArgFactory() };
                    }

                    callback.DynamicInvoke(args);
                    raised = true;
                }
#pragma warning disable CA1031 // Do not catch general exception types
                catch (Exception ex)
                {
                    ex.Data.Add("CallingDelegate", GetDescription(callback));
                    ExceptionManager.OnUnhandledException(ex);
                }
#pragma warning restore CA1031 // Do not catch general exception types
            }

            return(raised);
        }
Esempio n. 2
0
        /// <summary>
        /// Invokes the delegate given (if not <see langword="null" />) with protection against exceptions thrown by the
        /// invoked methods.
        /// </summary>
        /// <typeparam name="T">The type of the event args to pass.</typeparam>
        /// <param name="handler">The delegate to call.</param>
        /// <param name="eventName">The name of the event being raised.</param>
        /// <param name="sender">The object to pass as the sender.</param>
        /// <param name="usage">The way in which to use the factory to generate args.</param>
        /// <param name="eventArgFactory">A factory for generating event args.</param>
        /// <returns><see langword="true" /> if at least one delegate was successfully invoked, <see langword="false" /> otherwise.</returns>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Thrown if an invalid value passed in the <paramref name="usage" />
        /// argument.
        /// </exception>
        /// <exception cref="System.ArgumentNullException"><paramref name="eventName" /> is <see langword="null" />.</exception>
        /// <exception cref="System.ArgumentException">
        /// <paramref name="eventName" /> is zero-length or contains only white space
        /// characters.
        /// </exception>
        /// <exception cref="System.ArgumentNullException"><paramref name="eventArgFactory" /> is <see langword="null" />.</exception>
        public static bool SafeInvoke <T>([CanBeNull] this Delegate handler, [NotNull] string eventName, [CanBeNull] object sender, ArgsUsage usage, EventArgsFactory <T> eventArgFactory)
            where T : EventArgs
        {
            eventName.Validate(nameof(eventName), StringIs.NotNullEmptyOrWhiteSpace);
            eventArgFactory.Validate(nameof(eventArgFactory), ObjectIs.NotNull);

            // An empty invocation list is null.
            if (ReferenceEquals(handler, null))
            {
                return(false);
            }

            var delegates = handler.GetInvocationList();

            // All non-null invocation lists are also non-empty, so no need to test here.

            object[] args = null;
            bool     generateArgs;

            switch (usage)
            {
            case ArgsUsage.Reuse:
                args         = new[] { sender, eventArgFactory() };
                generateArgs = false;
                break;

            case ArgsUsage.UniqueInstance:
                generateArgs = true;
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(usage), usage, $@"'{nameof(usage)}' argument contains invalid value: {usage}.");
            }

            var raised          = false;
            var checkForHandled = typeof(HandledEventArgs).IsAssignableFrom(typeof(T));

            foreach (var callback in delegates)
            {
                try
                {
                    if (generateArgs)
                    {
                        args = new[] { sender, eventArgFactory() };
                    }

                    callback.DynamicInvoke(args);
                    raised = true;

                    if (checkForHandled && (((HandledEventArgs)args[1]).State == HandledEventState.HandledAndCeaseRaising))
                    {
                        break;
                    }
                }
                catch (ThreadAbortException)
                {
                    // Ignore thread abort exceptions, but do stop processing any further.
                    break;
                }
                catch (Exception ex)
                {
                    if (typeof(UnhandledExceptionEventArgs).IsAssignableFrom(typeof(T)))
                    {
                        // If we already in the process of raising an exception just ignore anything further.
                        continue;
                    }

                    // If an event handler has raised an exception report it, but carry on.
                    ExceptionManager.OnUnhandledException(ex, $@"Error whilst '{eventName}' event was being handled by {GetDescription(callback)}.");
                }
            }

            return(raised);
        }