简体   繁体   English

我应该抛出哪个异常来表示程序中的内部错误?

[英]Which Exception Should I Throw to Signal an Internal Error in my Program?

Which exception should I use when the program reaches a logic state that I "know" won't happen, and if it does, something is terribly bad?当程序达到我“知道”不会发生的逻辑状态时,我应该使用哪个异常,如果它发生了,那么事情非常糟糕?

For example:例如:

int SomeFunction(int arg) {
    SomeEnum x = Whatever(arg, somePrivateMember);
    switch (x) {
        case SomeEnum.Value1:
            return SomeFunction1();
        case SomeEnum.Value1:
            return SomeFunction2();
        default:
            throw new WhatTypeToThrow();
    }
}

Clearly, ArgumentException is a long-shot here since the invalid value for x could have come from a bug in Whatever(), or an invalid combination of any arguments and/or the current instance state.显然,这里的 ArgumentException 是一个长镜头,因为 x 的无效值可能来自 What() 中的错误,或者任何参数和/或当前实例状态的无效组合。

I'm looking for something such as an InvalidProgramStateException, InternalErrorException or similar.我正在寻找诸如 InvalidProgramStateException、InternalErrorException 之类的东西。

Of course I could define my own, but I wonder if there is a suitable exception in the framework.当然我可以自己定义,但是我想知道框架中是否有合适的例外。

Edit: Removed the simple sample code to reduce amount of ArgumentException answers.编辑:删除了简单的示例代码以减少 ArgumentException 答案的数量。

InvalidOperationException 呢?

Why not the InvalidEnumArgumentException?为什么不是 InvalidEnumArgumentException? It looks like it was designed specifically for this use-case.看起来它是专门为这个用例设计的。

Don't throw any specific exception type in the code you're looking at.不要在您正在查看的代码中抛出任何特定的异常类型。 Call Trace.Assert , or in this case even Trace.Fail to get a similar effect to Debug.Assert , except enabled even in release builds (assuming the settings aren't changed).调用Trace.Assert ,或者在这种情况下甚至Trace.Fail以获得与Debug.Assert类似的效果,除了在发布版本中启用(假设设置未更改)。

If the default trace listener, the one that offers a UI that offers to kill the whole program or launch a debugger, isn't appropriate for your needs, set up a custom trace listener in Trace.Listeners that causes a private exception type to be thrown whenever Trace.Fail is called (including when a Trace.Assert fails).如果默认跟踪侦听器(提供可Trace.Listeners整个程序或启动调试器的 UI)不适合您的需要,请在Trace.Listeners中设置自定义跟踪侦听器,从而导致私有异常类型为每当Trace.Fail时抛出(包括当Trace.Assert失败时)。

The exception type should be a private exception type because otherwise, callers may be tempted to try catching whatever exception type you're going to throw.异常类型应该是私有异常类型,否则,调用者可能会试图捕获您要抛出的任何异常类型。 For this particular exception type, you will want to make it as clear as possible that a future version of the method will no longer throw this particular exception.对于此特定异常类型,您将希望尽可能明确该方法的未来版本将不再抛出此特定异常。 You don't want to be forced to throw a TraceFailedException or whatever you call it from now until eternity to preserve backwards compatibility.您不想被迫抛出TraceFailedException或从现在到永远的任何您称之为它的东西,以保持向后兼容性。


Another answer mentioned Code Contracts already as an alternative.另一个答案提到 Code Contracts 已经作为替代方案。 It is, in a similar way: you can call Contract.Assert(false) .以类似的方式:您可以调用Contract.Assert(false) This takes the same approach of making it customisable what happens if an assertion fails, but in this case, the default behaviour is to throw an exception, again of a type that is not externally accessible.这采用了相同的方法来定制断言失败时发生的情况,但在这种情况下,默认行为是抛出异常,同样是外部无法访问的类型。 To make the most of Code Contracts, however, you should be using the static rewriter, which has both pros and cons that I will not get into here.然而,为了充分利用代码契约,您应该使用静态重写器,它既有优点也有缺点,我不会在这里介绍。 If for you the pros outweigh the cons, then by all means use it.如果对您来说利大于弊,那么一定要使用它。 If you prefer to avoid the static rewriter though, then I'd recommend avoiding the Contract class entirely, since it is not at all obvious which methods do and don't work.如果您更喜欢避免使用静态重写器,那么我建议您完全避免使用Contract类,因为根本不清楚哪些方法起作用和不起作用。

I think ArgumentOutOfRangeException is valid here and it's what I use.我认为 ArgumentOutOfRangeException 在这里是有效的,这就是我使用的。 It's the argument to the switch statement that is not handled as it's out of the range of handled values.由于超出了处理值的范围,因此不会处理 switch 语句的参数。 I tend to code it like this, where the message tells it like it is:我倾向于这样编码,消息告诉它就像这样:

switch (test)
{
    case SomeEnum.Woo:
        break;
    case SomeEnum.Yay:
        break;
    default:
    {
        string msg = string.Format("Value '{0}' for enum '{1}' is not handled.", 
            test, test.GetType().Name);

        throw new ArgumentOutOfRangeException(msg);
    }
}

Obviously the message is to your own tastes, but the basics are in that one.显然,该信息符合您自己的口味,但基础知识就在其中。 Adding the value of the enum to the message is useful not only to give detail concerning what known enum member was not handled, but also when there is an invalid enum ie the old "(666)SomeEnum" issue.将枚举的值添加到消息中不仅可以提供有关未处理的已知枚举成员的详细信息,而且在存在无效枚举(即旧的“(666)SomeEnum”问题)时也很有用。

Value 'OhNoes' for enum 'SomeEnum' is not handled.不处理枚举 'SomeEnum' 的值 'OhNoes'。

vs对比

Value '666' for enum 'SomeEnum' is not handled.不处理枚举 'SomeEnum' 的值 '666'。

Here are suggestions that I've been given:以下是我得到的建议:

  • ArgumentException : something is wrong with the value ArgumentException : 值有问题

  • ArgumentNullException : the argument is null while this is not allowed ArgumentNullException : 参数为空,而这是不允许的

  • ArgumentOutOfRangeException : the argument has a value outside of the valid range ArgumentOutOfRangeException : 参数的值超出了有效范围

Alternatively, derive your own exception class from ArgumentException .或者,从ArgumentException派生您自己的异常类。

An input is invalid if it is not valid at any time.如果输入在任何时候都无效,则输入无效 While an input is unexpected if it is not valid for the current state of the system (for which InvalidOperationException is a reasonable choice in some situations).如果输入对系统的当前状态无效,则输入是意外的(在某些情况下InvalidOperationException是合理的选择)。

See similar question and answer that I was given. 请参阅我得到的类似问题和答案。

You should consider using Code Contracts to not only throw exceptions in this case, but document what the failed assumption is, perhaps with a friendly message to the programmer.在这种情况下,您应该考虑使用代码契约不仅抛出异常,而且记录失败的假设是什么,也许向程序员提供友好的消息。 If you were lucky, the function you called ( Whatever ) would have a Contract.Ensures which would catch this error before you got your hands on it.如果你很幸运,你调用的函数( Whatever )会有一个Contract.Ensures ,它会在你得到它之前捕获这个错误。

program reaches a logic state that I "know" won't happen, and if it does, something is terribly bad.程序达到了我“知道”不会发生的逻辑状态,如果发生了,事情就非常糟糕了。

In this case, I would throw an ApplicationException, log what you can, and exit the app.在这种情况下,我会抛出一个 ApplicationException,记录你能记录的内容,然后退出应用程序。 If things are that screwed up, you certainly shouldn't try to recover and/or continue.如果事情搞砸了,您当然不应该尝试恢复和/或继续。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM