如何阻止异常破坏我的委托链?

时间:2022-07-26 07:27:04

I have come across what must be a common problem. When I have an event which may be subscribed to by several different classes, an exception thrown by one of these classes will kill the callback chain; as I do not know a priori in what order the callback is carried out, this can result in unpredictable state changes for some classes and not for others.

我遇到了一个常见的问题。当我有一个可能由几个不同的类订阅的事件时,这些类之一抛出的异常将终止回调链;因为我不知道回调执行顺序的先验,这可能导致某些类的不可预测的状态更改而不是其他类。

In the bible (CLR via C#, I am using C# 2.0) there is a short paragraph about using MulticastDelegate.GetInvocationList to get around this, but nothing more. So my question is: what is the best way to deal with this? Do I have to use MulticastDelegate.GetInvocationList every time I have an event?? Or do I need to enclose all methods which may be called as part of the delegate chain in some kind of rollback mechanism? Why are all these options so complicated in comparison to the simple event/delegate model which is so easy to use in C#? And how can I use the simple way without ending up with corrupted state?

在圣经中(CLR通过C#,我使用的是C#2.0)有一个关于使用MulticastDelegate.GetInvocationList来解决这个问题的简短段落,但仅此而已。所以我的问题是:处理这个问题的最佳方法是什么?每次有事件时我都必须使用MulticastDelegate.GetInvocationList吗?或者我是否需要在某种回滚机制中包含可能作为委托链的一部分调用的所有方法?与简单的事件/委托模型相比,为什么所有这些选项都如此复杂?我怎样才能使用简单的方法而不会以损坏的状态结束?

Thanks!

2 个解决方案

#1


If you simply invoke a delegate, it will call all the target methods in order. You need to use GetInvocationList if you want to execute them individually - for example:

如果只是调用委托,它将按顺序调用所有目标方法。如果要单独执行它们,则需要使用GetInvocationList - 例如:

  • to check Cancel after each
  • 在每次之后检查取消

  • to capture the return value of each
  • 捕获每个的返回值

  • to continue after failure of an individual target
  • 在个别目标失败后继续

As for the best way to use it: how do you want it to behave? It isn't clear to me... for example, this might suit an extension method quite well:

至于使用它的最佳方式:你希望它如何表现?我不清楚......例如,这可能非常适合扩展方法:

static void InvokeIgnoreErrors(this EventHandler handler,
        object sender) {
    if(handler != null) {
        foreach(EventHandler subHandler in handler.GetInvocationList()) {
            subHandler(sender, EventArgs.Empty);
        }
    }
}

Then you can just call myHandler.InvokeIgnoreErrors(this); (for example).

然后你可以调用myHandler.InvokeIgnoreErrors(this); (例如)。

Another example might be:

另一个例子可能是:

static bool InvokeCheckCancel(this CancelEventHandler handler,
        object sender) {
    if(handler != null) {
        CancelEventArgs args = new CancelEventArgs(false);
        foreach(CancelEventHandler subHandler in handler.GetInvocationList()) {
            subHandler(sender, args);
            if(args.Cancel) return true;
        }
    }
    return false;
}

which stops after the first event requests cancellation.

在第一个事件请求取消后停止。

#2


Rather than changing how you invoke events, I think you should review your event handlers. In my opinion event handler methods should always be written so they are "safe" and never let exceptions propagate. This is particularly important when you're handling events in GUI code where the event is invoked by external code, but a good habit to have at all times.

我认为您应该检查事件处理程序,而不是更改调用事件的方式。在我看来,应始终编写事件处理程序方法,以便它们是“安全的”,并且永远不会让异常传播。当您在GUI代码中处理事件由外部代码调用时,这一点尤为重要,但这是一个随时都有的好习惯。

#1


If you simply invoke a delegate, it will call all the target methods in order. You need to use GetInvocationList if you want to execute them individually - for example:

如果只是调用委托,它将按顺序调用所有目标方法。如果要单独执行它们,则需要使用GetInvocationList - 例如:

  • to check Cancel after each
  • 在每次之后检查取消

  • to capture the return value of each
  • 捕获每个的返回值

  • to continue after failure of an individual target
  • 在个别目标失败后继续

As for the best way to use it: how do you want it to behave? It isn't clear to me... for example, this might suit an extension method quite well:

至于使用它的最佳方式:你希望它如何表现?我不清楚......例如,这可能非常适合扩展方法:

static void InvokeIgnoreErrors(this EventHandler handler,
        object sender) {
    if(handler != null) {
        foreach(EventHandler subHandler in handler.GetInvocationList()) {
            subHandler(sender, EventArgs.Empty);
        }
    }
}

Then you can just call myHandler.InvokeIgnoreErrors(this); (for example).

然后你可以调用myHandler.InvokeIgnoreErrors(this); (例如)。

Another example might be:

另一个例子可能是:

static bool InvokeCheckCancel(this CancelEventHandler handler,
        object sender) {
    if(handler != null) {
        CancelEventArgs args = new CancelEventArgs(false);
        foreach(CancelEventHandler subHandler in handler.GetInvocationList()) {
            subHandler(sender, args);
            if(args.Cancel) return true;
        }
    }
    return false;
}

which stops after the first event requests cancellation.

在第一个事件请求取消后停止。

#2


Rather than changing how you invoke events, I think you should review your event handlers. In my opinion event handler methods should always be written so they are "safe" and never let exceptions propagate. This is particularly important when you're handling events in GUI code where the event is invoked by external code, but a good habit to have at all times.

我认为您应该检查事件处理程序,而不是更改调用事件的方式。在我看来,应始终编写事件处理程序方法,以便它们是“安全的”,并且永远不会让异常传播。当您在GUI代码中处理事件由外部代码调用时,这一点尤为重要,但这是一个随时都有的好习惯。