/// <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); }
/// <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); }