2009-07-21 51 views
2

请考虑以下程序。它显示的行为如何(即异常会传播出事件处理程序)是一件“好事”?据我所知,它只能是坏的;意想不到的异常从他们不应该出现的函数中弹出。在我的特殊情况下,这是杀死线程。那么,在某些情况下,这种行为实际上是一件好事吗?这是否有理由说,让事件处理程序排除异常是不好的设计?为什么异常传播出事件处理程序?

static class Program { 
    static void Main() 
    { 
     Foo foo = new Foo(); 
     foo.SomeEvent += ThrowException; 

     try 
     { 
      foo.OnSomeEvent(); 
     } 
     catch (Exception) 
     { 
      // This is printed out 
      Console.WriteLine("Exception caught!"); 
     } 
    } 

    static void ThrowException(object sender, EventArgs e) 
    { 
     throw new Exception(); 
    } 
} 

// Define other methods and classes here 
class Foo 
{ 
    public event EventHandler SomeEvent; 

    public void OnSomeEvent() 
    { 
     SomeEvent(this, EventArgs.Empty); 
    } 
} 

回答

14

你最喜欢的选择是什么 - 静静吞咽异常?我根本不喜欢那样。

事件是只是一种实现观察者模式的方式,真的。如果一个监听器抛出一个异常,我绝对期望这个异常被抛回给调用者。我能想到的任何其他行为都将有效地将异常视为不重要。例外的一点是,当出现问题时,您可以快速隐含地了解它。你必须明确地处理异常,这样你就不会在没有意识到的情况下在腐败的状态下继续你的快乐。

你提出了一个关于谁负责处理异常的有效观点。一般来说,我认为最好假定几乎任何事情都可以在任何时候抛出异常。除了特殊情况,我知道一个特定的例外可能发生,我可以处理它,除了可能包括和重新抛出,或者记录和重新抛出之外,我通常不会捕获例外情况下的异常。

现在它可能是你的事件处理程序的一个真正不应被抛出一个异常 - 他们还没有真正碰到一个错误条件 - 但如果它是一个完全合理的例外,这表明了严重的会发生什么问题?虽然程序崩溃是丑陋的,它往往比它的一些故障持续好转,可能损坏持续状态等

从根本上说,我不认为CS/SE领域已经得到了错误处理“权”呢。我甚至不确定是不是这是一种在所有情况下都很容易表达的正确事情的优雅方式......但我希望目前的情况不如现在这样好。

+0

+ 9000最后一款! – 2009-07-21 06:38:30

+0

下面是对此的一个扩展:是否可以接收'Exception'?想到我当前项目的一个特殊情况是处理用户生成的脚本。假设他们编译的时候,当我尝试运行它们时,我有一个try/catch(Exception)块来调用刚创建的方法。当然,如果发生错误,它会向用户报告,以便他们修复脚本,但是一起崩溃真的是对的? – 2009-07-21 06:44:44

+1

这是捕获(例外)的少数正确用途之一。理想情况下,您可以在单独的进程中运行用户脚本,以免干扰您的程序。如果脚本崩溃,您只需获得类似-1073741819之类的退出代码。 – 2009-07-21 06:57:28

0

您需要与事件处理是否能抛出异常事件源的合同。例如,如果一个COM对象是一个事件源,这是严格禁止的 - 异常不应该跨越COM边界。

+0

回复:我的最后一个问题:除非API说你可以抛出异常,BException等,否则这个契约是否应该一直是事件处理程序不应抛出异常(或允许未捕获异常)? – 2009-07-21 06:14:15

+0

这个合同可以是方便的。如果事件源在.NET中,则期望处理程序可以抛出任何.NET异常是完全合理的。 – sharptooth 2009-07-21 06:24:43

1

我个人的偏见是,没有捕捉异常通常是一件坏事。对我而言,这条规则唯一的“例外”是对于未捕获的异常终止流程的简单应用,您会看到错误并进行修复。

对于多线程应用程序,如果未捕获异常的默认行为是zap线程,那么在我看来,荒谬的是不会捕获异常。因此,事件处理程序不应该只是将这个例外放在堆栈上,并希望最好。

无声吞咽异常通常也不好,有些不好的事情已经发生,需要修复。所以也许记录一条消息,然后返回?

+0

是不是永远正确的捕捉'Exception'呢?即。在处理线程的顶层,以便在进行完整性检查后保持活动状态。 – 2009-07-21 06:25:48

+0

我宁愿崩溃,重新启动并发送日志。此外,一些例外,你不能赶上(内存溢出,StackOverflow的和ThreadAbort)。 – 2009-07-21 06:39:42

+0

还有更多的被添加到列表抓不到在CLR 4 – 2009-07-21 06:42:19

0

事件只不过是一个函数调用的语法糖,所以它传播到引发事件的函数是有意义的。什么,否则,这种行为应该是什么?例外必须去某个地方。

2

这里讨论的异常处理的主要方面是:如果你不知道如何处理它,不要捕获异常。但让我们来谈谈观察者模式,其中通告者发出有关状态变化的事件(或信号),并由侦听器处理。通知程序的一个很好的例子是一个发射'点击'事件的按钮。按钮是否关心谁是听众以及他们在做什么?不是真的。如果你是一个听众,并且你有这个事件,那么你有一份工作要做。如果你不能这样做,你必须处理这个错误或通知用户,因为传递异常到按钮没有任何意义 - 按钮绝对不知道如何处理侦听器作业的错误。并且按钮状态改变器(可能是一些消息循环)也不会 - 你的应用程序最终会在Main()发生崩溃。

这就是观察者模式的工作原理 - 事件发射器不知道他们的听众正在做什么,他们很少有机会正确处理这个异常。

还要记住,如果您的异常处理程序抛出异常,则可能有其他侦听器不会收到通知,并且可能导致未定义的应用程序状态。

所以我的建议是在事件处理程序中捕获所有异常,但要弄清楚如何在那里处理它们。否则没有人会。

0

将事件监听器作为您的合同的一部分,最好将异常考虑在内。

这意味着,如果侦听器很好,并捕获它的Exceptions(它可以为已知的那些做),那么你就可以。

对于其余的未知异常或Java语音“运行时异常”,您需要为它们做好准备,就好像它们会出现在代码中一样。

我想说的是,如果您正在与事件侦听器建立契约,那么您不能强制它们只抛出一个异常类型(以便您可以使用它们),并且您需要认真对待所有异常。毕竟,它们是你不想为消费者隐瞒的“错误状态”的迹象。

相关问题