C#:抛出自定义异常最佳实践

47 c# exception custom-exceptions

我已经阅读了一些关于C#异常处理实践的其他问题,但似乎没有人问我在寻找什么.

如果我为特定的类或一组类实现我自己的自定义Exception.是否应该使用内部异常将与这些类相关的所有错误封装到我的异常中,还是应该让它们落空?

我认为捕获所有异常会更好,以便可以从我的源代码中立即识别异常.我仍然将原始异常作为内部异常传递.另一方面,我认为重新抛出异常是多余的.

例外:

class FooException : Exception
{
    //...
}
Run Code Online (Sandbox Code Playgroud)

选项1:Foo包围所有异常:

class Foo
{
    DoSomething(int param)
    {
        try 
        {
             if (/*Something Bad*/)
             {  
                 //violates business logic etc... 
                 throw new FooException("Reason...");
             }
             //... 
             //something that might throw an exception
        }
        catch (FooException ex)
        {
             throw;
        }
        catch (Exception ex)
        {
             throw new FooException("Inner Exception", ex);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

选项2:Foo抛出特定的FooExceptions但允许其他异常落空:

class Foo
{
    DoSomething(int param)
    {
        if  (/*Something Bad*/)
        {
             //violates business logic etc... 
             throw new FooException("Reason...");
        }
        //... 
        //something that might throw an exception and not caught
    }
}
Run Code Online (Sandbox Code Playgroud)

Tes*_*rex 54

根据我对图书馆的经验,您应该将所有内容(您可以预期)包装在一起,FooException原因如下:

  1. 人们知道它来自你的课程,或者至少来自他们的使用.如果他们看到FileNotFoundException他们可能正在寻找它.你正在帮助他们缩小范围.(我现在意识到堆栈跟踪就是为了这个目的,所以也许你可以忽略这一点.)

  2. 您可以提供更多上下文.用您自己的例外包装FNF,您可以说"我试图为此目的加载此文件,但找不到它.这暗示了可能的正确解决方案.

  3. 您的库可以正确处理清理.如果你让异常气泡,你就强迫用户清理.如果你已经正确地封装了你正在做的事情,那么他们就不知道如何处理这种情况!

请记住只包装您可以预期的例外情况,例如FileNotFound.不要只是包装Exception并希望最好的.

  • 我认为这个答案的重要部分是如果你提供更多的背景.如果您没有提供这个额外的上下文,那么让异常通过不变. (14认同)

Tim*_*ter 18

看看这个MSDN最佳实践.

考虑使用throw而不是throw ex如果你想重新抛出捕获的异常,因为这样原始的堆栈跟踪保持不变(行号等).

  • @TimSchmelter:很好的信息,但这不是这个问题的相关答案.看来你在标题后停止了阅读.在这种情况下,您的建议没有任何结果; 你无缘无故地抓住了这个例外(你的`throw;`相当于OP的Option 2,因为他没有做任何额外的处理). (3认同)

DOK*_*DOK 6

在创建自定义异常时,我总是添加一些属性.一个是用户名或ID.我添加了一个DisplayMessage属性来携带要显示给用户的文本.然后,我使用Message属性来传达要记录在日志中的技术细节.

我捕获数据访问层中的每个错误,我仍然可以捕获存储过程的名称和传递的参数值.或内联SQL.也许是数据库名称或部分连接字符串(请不要凭据).这些可以在Message或他们自己的新自定义DatabaseInfo属性中.

对于网页,我使用相同的自定义异常.我将在Message属性中输入表单信息 - 用户在网页上的每个数据输入控件中输入的内容,正在编辑的项目的ID(客户,产品,员工,等等)以及用户的操作异常发生时正在服用.

所以,根据你的问题,我的策略是:只有在我可以对异常做些什么时才会抓住.通常,我所能做的就是记录细节.所以,我只是抓住那些细节可用的点,然后重新抛出让异常泡到UI.我在自定义异常中保留了原始异常.