将单个自定义异常与枚举组合以轻松创建多个子异常

时间:2014-08-05 17:02:38

标签: c# .net exception exception-handling c#-5.0

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

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

我们说我创建了这个枚举和自定义异常:

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
    }
}

在这样的实施中需要注意什么?优点和缺点?从我的立场来看,我只看到好东西。但这通常是一种错觉。

3 个答案:

答案 0 :(得分:0)

你的approch没问题,只要所有ExceptionKinds都处于同一级别:所有异常都可以在同一层上处理,并导致相同的处理(如显示消息框或停止任务)。 但是,例如,当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??
   }
}

这看起来不是什么大问题,但是在可能引入问题的重负载上,因为捕获异常需要花费时间,并且在不需要处理它的情况下捕获异常,并且只是为了重新抛出它的目的,可能会变慢关闭你的申请。

顺便说一句:我不明白为什么你选择让ExceptionKind属性返回可以为空的值,因为它永远不会为空。

答案 1 :(得分:0)

请记住,编写几个不同的例外类是您付出的努力一次;处理不同的异常情况可能会出现很多次。因此,请尽量集中精力使客户能够更轻松地处理这些例外。

比较捕获一般异常的样板,偷看内部以检查它是否真正相关,否则重新抛出,而不是仅仅捕获您知道自己关心的特殊异常类型。捕获特定异常类型和派生新异常类型的工具一起工作,使得使用专门的异常类型比使用标记有特殊值的常规异常类型更容易。不要反对这种语言。

如果您预计您的客户希望在同一站点捕获所有自定义异常,则可以使您的自定义异常继承自常见的MyCustomException类。这基本上是你的枚举解决方案的超集;如果有人捕获了一般的MyCustomException类,然后由于某种原因需要知道特定的类型,他们可以说(ex是MyCustomInitializationException)。但是,如果这是他们关心的,那么您仍然可以让用户首先捕获MyCustomInitializationException。

答案 2 :(得分:0)

如果异常与不同的代码(如HttpStatus)真的相同,那么它是有道理的。否则我会创建不同的异常类。