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

C# 将单个自定义异常与枚举相结合,以轻松创建多个子异常,c#,.net,exception,exception-handling,c#-5.0,C#,.net,Exception,Exception Handling,C# 5.0,我想我需要在C#5.0中编写一个或多个自定义异常。我可能不是,但似乎.NET提供的异常属于一个过于系统化和过于通用的异常域——非常特定于“在CLR支持文件I/O上有一个正在运行的程序”的情况。当然,这正是我所拥有的。尽管如此,在尝试用C#或任何其他OO语言开发应用程序时,您创建的新类型的子集(或全部)在大多数情况下都应该来自概念本体,而概念本体与CLR或.NET Framework这样的系统领域相比还很不接近。这就是我如何看待OO开发中的“创作”部分——但这确实是一个完全不同的问题 因此,关于创

我想我需要在C#5.0中编写一个或多个自定义异常。我可能不是,但似乎.NET提供的异常属于一个过于系统化和过于通用的异常域——非常特定于“在CLR支持文件I/O上有一个正在运行的程序”的情况。当然,这正是我所拥有的。尽管如此,在尝试用C#或任何其他OO语言开发应用程序时,您创建的新类型的子集(或全部)在大多数情况下都应该来自概念本体,而概念本体与CLR或.NET Framework这样的系统领域相比还很不接近。这就是我如何看待OO开发中的“创作”部分——但这确实是一个完全不同的问题

因此,关于创建自定义异常的“问题”,我想听听下面的解决方案是否有任何缺点

假设我创建了此枚举和自定义异常:

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
    }
}
公共类SomeType
{
公共无效星号(整数)
{
如果(num!=42)
{
抛出新的MyCustomException(
MyCustomExceptionKind.AttemptToStart,
“不可能启动这该死的东西……”,
无效);
}
}
公共布尔幽会艺术(整数)
{
tryOK=true;
尝试
{
星体诗(num);
}
捕获(MyCustomException ex)
{
//将整体状态布尔设置为false:
tryOK=false;
//使用开关案例处理此异常的子类型:
开关(MyCustomException.ExceptionKind)
{
案例MyCustomExceptionKind.MyCustomStartException:
Trace.TraceError(“哦,天哪,无法启动”);
打破
}
}
返回tryOK;
}
静态公用主(字符串[]参数)
{
var myObj=新的SomeType();

myObj.TryStart(199);//只要所有异常类型都在同一级别上,您的方法就可以了:所有异常都可以在同一层上处理,并将导致相同的处理(如显示消息框或停止任务)。 但例如,当
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
属性返回可为null的值,因为它永远不能为null。

请记住,编写几个不同的异常类是你付出的努力,你只需一次;处理不同的异常情况可能会出现很多次。因此,请试着把重点放在使这个属性为空上对于将要处理这些异常的客户机来说,这是一个更好的选择

比较捕获一般异常、窥视内部以检查其是否实际相关、重新引用其他异常的样板文件,与只捕获您知道您关心的特殊异常类型的样板文件。捕获特定异常类型和派生新异常类型的功能协同工作,使仅使用sp变得更容易特殊化异常类型,而不是使用一般异常类型,否则用特殊值标记。不要违背语言的粒度


如果您预期您的客户机希望在同一站点捕获所有自定义异常,则可以使自定义异常从公共MyCustomException类继承。这本质上是枚举解决方案的超集;如果有人捕获了常规MyCustomException类,然后出于任何原因需要知道特定的异常pe,他们可以说(例如MyCustomInitializationException)。但如果用户关心的是MyCustomInitializationException,那么您仍然首先为用户提供了捕获MyCustomInitializationException的选项。

如果这些异常实际上是相同的,但代码不同(如HttpStatus),那么它就有意义了。否则我会创建不同的异常类。

您真的需要根据异常类型执行不同的操作吗?事实上,当您的自定义异常与任何其他异常抛出时,您真的需要执行不同的操作吗?@JohnSaunders我可能不会在switc中使用多个案例h在任何异常处理程序中。其想法是,我可以在另一个类的方法中为不同的异常处理程序应用同一个异常类。因此,我不必为该另一个类中的任何特殊子异常创建新类型。是的,可能会发生特殊子异常。主要基于错误的对象状态,而不是错误的用户输入。但是您需要根据错误的对象状态进行不同的操作吗?为什么不使用一个“FaultyObjectStateException”?谢谢。您对Nullable的使用是正确的-我想我真的不需要它。虽然属性不返回Nullable类型,但只返回值。我想我希望能够将枚举类型初始化为除其中一个值以外的其他值(未初始化的值仍默认为第一个值)。因此,null成为了我的选择。但这在这里不是必需的,对。是的,好的。我发现自己有时没有携带特定的异常类型。我有时只是捕获异常()并将某些代码失败的事件转换为bool(tryX()/X()模式)