将单个自定义异常与枚举相结合,可以轻松创建多个子异常
本文关键字:创建 异常 自定义异常 单个 枚举 相结合 | 更新日期: 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类。