Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/287.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#:抛出自定义异常最佳实践_C#_Exception_Custom Exceptions - Fatal编程技术网

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

C#:抛出自定义异常最佳实践,c#,exception,custom-exceptions,C#,Exception,Custom Exceptions,我已经阅读了一些关于C#异常处理实践的其他问题,但似乎没有人问我在寻找什么 如果我为一个特定类或一组类实现自己的自定义异常。应该使用内部异常将所有与这些类相关的错误封装到我的异常中,还是让它们失效 我想最好捕获所有异常,以便可以立即从我的源代码中识别异常。我仍然将原始异常作为内部异常传递。另一方面,我认为重新引用异常是多余的 例外情况: class FooException : Exception { //... } class Foo { DoSomething(int pa

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

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

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

例外情况:

class FooException : Exception
{
    //...
}
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);
        }
    }
}
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
    }
}
选项1:Foo封装所有异常:

class FooException : Exception
{
    //...
}
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);
        }
    }
}
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
    }
}
选项2:Foo抛出特定的Foo异常,但允许其他异常通过:

class FooException : Exception
{
    //...
}
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);
        }
    }
}
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
    }
}

自定义异常的目的是向stacktrace提供详细的上下文信息,以帮助调试。选项1更好,因为没有它,如果异常发生在堆栈中的“较低”位置,则无法获得异常的“来源”。

如果在Visual Studio中运行“异常”的代码段,则可以获得编写异常的良好实践模板

注意 选项1:您的
抛出新的FooException(“原因…”)不会被捕获,因为它位于try/catch块之外

  • 您应该只捕获要处理的异常
  • 如果没有向异常添加任何附加数据,请使用
    抛出因为它不会杀死堆栈。在选项2中,您仍然可以在catch内部进行一些处理,只需调用
    throw以使用原始堆栈重新显示原始异常

  • 选择2是最好的。我相信最佳实践是,当您计划对异常进行处理时,只捕获异常


    在这种情况下,选项1只是用您自己的异常包装一个异常。它不会增加任何值,类的用户也不能仅仅捕获ArgumentException,例如,他们还需要捕获FooException,然后对内部异常进行解析。如果内部异常不是一个异常,他们可以做一些有用的事情,他们将需要重新调用。

    根据我对库的经验,出于以下几个原因,您应该将所有(您可以预期的)内容包装在
    footexception
    中:

  • 人们知道它来自于你的课堂,或者至少是他们对它们的使用。如果他们看到
    FileNotFoundException
    他们可能正在到处寻找它。你在帮助他们缩小范围。(我现在意识到堆栈跟踪可以达到这个目的,所以您可以忽略这一点。)

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

  • 你的库可以正确地处理清理。如果你让异常冒泡,你就是在强迫用户清理。如果你正确地封装了你正在做的事情,那么他们就不知道如何处理这种情况

  • 请记住只包装您可以预期的异常,如
    FileNotFound
    。不要只包装
    Exception
    ,并期待最好的结果。

    看看这个


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

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

    我捕获数据访问层中的每一个错误,在这个级别上,我仍然可以捕获存储过程的名称和传递的参数值。或者内联SQL。可能是数据库名称或部分连接字符串(请不要凭据)。这些可能会出现在消息中,也可能出现在它们自己的新自定义DatabaseInfo属性中

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


    因此,根据你的问题,我的策略是:只有在我可以对异常做一些事情时才能捕获。通常,我所能做的就是记录详细信息。因此,我只在这些详细信息可用的地方捕获,然后重新搜索,让异常冒泡到UI。我在自定义异常中保留原始异常。

    最重要的是在捕获异常时,代码需要知道的重要一点是系统的状态相对于它“应该”的状态(可能是因为出现了错误而抛出异常),异常不幸地完全从异常对象中丢失。如果LoadDocument方法中出现错误,则可能文档未成功加载,但至少有两种可能的系统状态:

  • 系统状态可能好像从未尝试过加载。在这种情况下,如果应用程序可以在没有加载文档的情况下继续加载,则完全可以继续。
  • 系统状态可能已充分损坏,最好的做法是将可以保存的内容保存到“恢复”文件中(避免用可能损坏的数据替换用户的好文件)并关闭。 显然,在这两个极端之间往往会有其他可能的状态。我建议,人们应该努力制定一个明确表明状态1存在的自定义例外,如果可预见但不可避免的情况可能导致状态1存在,则可能有一个针对状态2的例外。任何发生并将导致状态1的例外都应该是wra加入一个例外