将单个自定义异常与枚举相结合,可以轻松创建多个子异常

本文关键字:创建 异常 自定义异常 单个 枚举 相结合 | 更新日期: 2023-09-27 18:16:36

我认为我需要在c# 5.0中编写一个或多个自定义异常。我可能不是,但是。net提供的异常似乎属于一个过于系统化和过于通用的异常领域——非常特定于"在支持文件I/O的CLR上运行程序"的情况。这正是我所拥有的,至少,当然。尽管如此,当尝试用c#或任何其他面向对象语言开发应用程序时,您创建的新类型的子集(或全部)应该在大多数情况下源于概念本体,这与CLR或。net框架这样的系统领域相去甚远。这就是我对OO开发中"创作"部分的看法——但这实际上是一个完全不同的问题。

所以,关于创建自定义异常的"问题",我想听听下面的解决方案是否有任何缺点。

假设我创建了这个enum和自定义异常:

public enum MyCustomExceptionKind
{
    MyCustomInitializationException,
    MyCustomStartException,
    MyCustomStopException,
    MyCustomFatalException
}
public class MyCustomException: Exception
{
    private MyCustomExceptionKind? m_exceptionKind = null;
    public MyCustomExceptionKind ExceptionKind
    {
        // return the value of the nullable type:
        get { return m_exceptionKind.Value; }
    }
    // let's support only the most-exclicit instance constructor for now:
    public EmployeeListNotFoundException(
        MyCustomExceptionKind myCustomExceptionkind,
        string message,
        Exception inner): base(message, inner)
    {
        m_exceptionKind = myCustomExceptionkind;
    }
}

这里的思想是使用内置枚举类型。我没有创建许多新的异常,而是选择使用枚举对异常的子类型进行编码。注意,我还通过使用问号使用了一个可空的枚举类型。

处理这样的异常会像这样:

public class SomeType
{
    public void StartUniverse(int num)
    {
        if (num != 42)
        {
            throw new MyCustomException(
                MyCustomExceptionKind.AttemptToStart,
                "It was not possible start the damn thing ...",
                null);
        }
    }
    public bool TryStart(int num)
    {
        tryOK = true;
        try
        {
            StartUniverse(num);
        }
        catch (MyCustomException ex)
        {
            // set the overall state-bool to false:
            tryOK = false;
            // use a switch case for handling sub-types of this exception:
            switch (MyCustomException.ExceptionKind)
            {
                    case MyCustomExceptionKind.MyCustomStartException:
                    Trace.TraceError("oh dear - could not start");
                    break;
            }
        }
        return tryOK;
    }
    static public Main(string[] args)
    {
        var myObj = new SomeType();
        myObj.TryStart(199); // <-- 199 != 42
    }
}

在这种实现中有什么要注意的吗?利与弊?在我看来,我只看到好的一面。但这通常是一种错觉。

将单个自定义异常与枚举相结合,可以轻松创建多个子异常

请记住,编写几个不同的异常类是您付出的努力一次;处理不同的异常场景可能会出现很多次。因此,请努力使处理这些异常的客户端更容易。

比较捕获一般异常的样板文件,检查它是否真的相关,然后重新抛出,而不是捕获一个你知道你关心的特殊异常类型。捕获特定异常类型和派生新异常类型的工具一起工作,使使用专门化异常类型比使用标记有特殊值的一般异常类型更容易。不要违背语言的本质。

如果你预期你的客户端想要在同一站点捕获你所有的自定义异常,你可以让你的自定义异常从一个公共的MyCustomException类继承。这实际上是枚举解的超集;如果有人捕获了一般的MyCustomException类,然后出于某种原因需要知道具体的类型,他们就可以说(ex是MyCustomInitializationException)。但是,如果用户关心MyCustomInitializationException,那么您仍然可以在一开始就为他们提供捕获MyCustomInitializationException的选项。

你的方法是可以的,只要所有的exceptiontypes都在同一层上:所有的异常都可以在同一层上处理,并将导致相同的处理(如显示消息框或停止任务)。但是,例如,当MyCustomFatalException应该导致程序终止时,那么您的方法就不是最好的,因为您需要捕获它两次

void DoStuff()
{
    try
    {
       ...
    }
    catch(MyCustomException inner)
    {
       if (inner.ExceptionKind == MyCustomExceptionKind.MyCustomFatalException)
          throw;
       // handle inner, show message box
    }
}
void DoStuffTwice()
{
   DoStuff();
   DoStuff();
}    

void Main()
{
   try
   {
       DoStuffTwice();
   }
   catch(MyCustomException inner)
   {
       if (inner.ExceptionKind == MyCustomExceptionKind.MyCustomFatalException)
          return;
       // now what??
   }
}

这看起来没什么大不了的,但是在负载很重的情况下,这可能会带来问题,因为捕获异常需要花费时间,并且捕获异常而不需要处理它,只是为了重新抛出它,这可能会减慢应用程序的速度。

BTW:我不明白为什么你选择使ExceptionKind属性返回可空值,因为它永远不能为空。

如果异常确实是相同的东西,不同的代码(如HttpStatus),那么它是有意义的。否则我将创建不同的Exception类。