在C#的switch语句中,是否应该抛出异常?

9

我有一个插入查询,返回一个整数。根据这个整数,我可能想要抛出一个异常。在switch语句中这样做是否合适?

 switch (result)
        {

            case D_USER_NOT_FOUND:
                throw new ClientException(string.Format("D User Name: {0} , was not found.", dTbx.Text));
            case C_USER_NOT_FOUND:
                throw new ClientException(string.Format("C User Name: {0} , was not found.", cTbx.Text));
            case D_USER_ALREADY_MAPPED:
                throw new ClientException(string.Format("D User Name: {0} , is already mapped.", dTbx.Text));
            case C_USER_ALREADY_MAPPED:
                throw new ClientException(string.Format("C User Name: {0} , is already mapped.", cTbx.Text));
            default:

                break;
        }

我通常在switch语句中添加break语句,但它们不会被执行。这是一种不好的设计吗?请与我分享任何意见/建议。

谢谢, ~来自圣地亚哥的ck


2
看起来你正在尝试将返回代码映射到异常。你的意图很明确,所以我认为这个解决方案是可以的。 - Captain Sensible
8个回答

12
为什么不这样做呢?
摘自Anders Hejlsberg等人的《C#编程语言第三版》第362页:
一个switch部分的语句列表通常以break、goto case或goto default语句结束,但允许使用任何使语句列表的终点无法到达的构造。类似地,throw或return语句始终会转移控制权并且永远不会到达其终点。因此,以下示例是有效的:
switch(i) {
case 0:
    while(true) F();
case 1:
    throw new ArgumentException();
case 2:
    return;
}

4

没有问题,为什么会造成不良设计呢?

或者,由于异常类型在所有的case中都是相同的,你可以构建一个错误消息查找表来避免一些代码重复。例如:

static private Dictionary<int, string> errorMessages;
static
{
    // create and fill the Dictionary
}

// meanwhile, elsewhere in the code...
if (result is not ok) {
    throw new ClientException(string.Format(errorMessages[result], cTbx.Text, dTbx.Text));
}

在消息本身中,您可以使用{0}{1}等选择适当的参数。

3
我不太赞同你的变量命名规则;), 但我不认为你所做的有什么不妥。这似乎是一种将一个媒介中的错误转化为另一个媒介的相当优雅的方式。
我假设你的“插入查询”是某种存储过程。

1
如果可能的话,最好将失败结果设置在throw的位置,否则您最终需要进行结果检查和throw。但当然可能不行。
另外,我会将错误字符串转换为带有占位符的资源或常量,这样如果要更改措辞,就不必更改多个地方。

0

你所采取的方法没有问题。Switch语句比if / then语句更易于阅读(并且可能更快)。你可以做的另一件事是在中括号里加载可能的异常。

Dictionary<Result_Type, Exception>

然后从那里抛出异常。如果您有很多开关语句,这将使代码更紧凑(如果需要,您可以在运行时添加和删除它们)。


一个字典可能是不错的选择,但它必须是 Dictionary<Result_Type, Func<Exception> 或者类似于 Dictionary<Result_Type, ExceptionFactory>Dictionary<Result_Type, IExceptionFactory> 这样的东西,并且需要在某个地方定义一个合适的异常工厂类(和接口,如果适用)。抛出现有的异常对象将至少破坏堆栈跟踪。如果两个线程同时遇到相同类型的异常,则在它们之间共享异常对象可能会导致一个线程的处理程序看到另一个线程的堆栈跟踪。 - supercat
是的,这不是使用字典的最佳示例。如果有什么需要,你可能需要存储异常类型,而不是实际的异常本身。 - kemiller2002
不幸的是,存储异常类型并没有太多用处,因为除非使用反射(在异常的上下文中有点不可靠),否则没有通用的方法可以根据异常类型生成该类型的异常。 - supercat

0

我认为这是可以的。看起来你正在使用 switch 语句将返回代码映射到异常。只要情况不太多,这就不是问题。


0

在你的情况下使用 switch 没有任何问题。

更强烈的考虑应该放在异常本身是否合适上。通常,只有在出现超出预期行为范围的情况下才应使用异常。异常不应用作程序流程逻辑。根据我看到的代码,在你的情况下,你可能可以使用它们。


0
也许我和这里的所有答案都有不同的看法。
我不想在代码中切换,我宁愿将 "result" 传递给 "ClientException" 类,让它决定需要显示什么字符串,而不是到处都有一个丑陋的开关来创建各种消息。
我的代码应该是:
throw new ClientException(result, cTbx.Text);

所以,即使你可以在switch...case中抛出错误,但我的看法是你可以避免这一切。


1
客户端异常构造函数在哪个点会有相同的开关逻辑,但必须有中断。 - Yishai
我进行了负投票,因为这个“解决方案”只是转移问题,并没有实质性的改变。 - Captain Sensible
@Seventh Element - 你说得没错,这样做并没有真正的区别,但是在我的解决方案中,代码更易于维护,因为switch...case在ClientException类内部,调用者不需要在他们的代码中一遍又一遍地编写switch...case。 - Sunny
除了@Yishai所说的观点外,结果可能并不总是一个错误代码。 - Rowland Shaw
@Rowland - 在成功情况下,我确定不会抛出异常,我的例子是为了异常处理。我希望我没有在任何方面传达错误信息。 - Sunny
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接