假设我们有SomeEnum
,并有一个switch语句处理它,如下所示:
enum SomeEnum
{
One,
Two,
}
void someFunc(SomeEnum value)
{
switch(value)
{
case SomeEnum.One:
...
break;
case SomeEnum.Two:
...
break;
default:
throw new ??????Exception("Unhandled value: " + value.ToString());
}
}
正如你所看到的,我们处理了所有可能的枚举值,但仍然保留了一个默认值,在添加新成员的情况下抛出一个异常,我们希望确保我们知道缺少的处理。
我的问题是:在这种情况下,你想通知给定的代码路径没有被处理/实现或者从来没有被访问过,什么是正确的异常?我们曾经使用NotImplementedException
,但它似乎不适合。我们的下一个候选项是InvalidOperationException
,但这个术语听起来不对。什么是正确的,为什么?
编辑:C# 8.0引入了switch expressions,它会对非exahustive switch语句产生编译器警告。这也是为什么在适用的情况下应该使用switch表达式而不是switch语句的另一个原因。同样的函数可以用更安全的方式编写,比如:
void someFunc(SomeEnum value)
{
_ = value switch
{
SomeEnum.One => ....,
SomeEnum.Two => ....,
}
}
当一个新成员被添加到SomeEnum
时,编译器将显示警告 “CS 8509:开关表达式不处理其输入类型的所有可能值(它不是穷举的)。例如,模式'EnumHandling.SomeEnum.Three'不包括开关表达式的.",这使得捕获潜在的错误变得更容易。
6条答案
按热度按时间olmpazwi1#
ArgumentException
在这个示例中看起来最正确(尽管在BCL中没有定义)。枚举参数有一个特殊的例外-InvalidEnumArgumentException:
使用作为枚举数的无效参数时引发的异常。
另一种选择是ArgumentOutOfRangeException:
当参数的值超出被调用方法定义的允许值范围时引发的异常。
使用这些的逻辑是传入的参数(
value
)对于someFunc
来说是无效的。daupos2t2#
我会抛出
InvalidEnumArgumentException
,因为它将给予更详细的信息,在这种情况下,您正在检查枚举0x6upsns3#
因为你在函数中有登录名,你可以抛出InvalidArgumentException。
当一个无效的参数被传递到服务器的引用连接上的方法**时引发的异常。
**编辑:**一个更好的选择是:ArgumentException,因为
Microsoft.SqlServer.Management.Common
命名空间中有InvalidArgumentException
。例如:nuypyhwy4#
InvalidArgumentException。当用户传递了一些无效的值或null值时,如果需要value,建议处理InvalidArgumentException。
roqulrg35#
如果你正在使用Code Contracts(我强烈推荐),你应该把它放在方法的开头:
如果你想检查一个枚举的范围,该范围有太多的单独值而无法显式地写入它们,你可以这样做:
jucafojl6#
它们是接口的合约的一部分,所以这完全取决于你想要交流什么;什么是“意图”。我能想到三种不同的情况:
1.未处理的情况是一个 * 实现错误 *,也就是说,它应该被认为是一个bug,永远不会发生。
1.我从来没有打算支持这个案子。
1.未处理的情况下可以在实践中工作,但 * 目前不支持 *。
在前两种情况下,一个理想的API将 * 防止 * 异常发生。在第三种情况下,例外可能更适合作为维护更清晰的API的一种方式,这样可以更容易地发展。
更详细地考虑这三种情况:
NotImplementedException
很好地传达了这一点。但是,这不是您会提交到稳定分支或希望在生产代码中看到的东西。而且,作为一个一般的经验法则,只要语言特性可以帮助您避免编写异常,就使用它们。正如OP在编辑中指出的那样,C# 8.0's switch expressions服务于相同的目的。您将得到一个编译错误,而不是运行时错误。1.我个人更喜欢
ArgumentException
。但是,我也会考虑是否可以通过在类型系统中嵌入相同的信息来防止异常。例如,删除枚举值,或创建一个作为原始枚举子集的用途受限枚举。NotSupportedException
通常用于更抽象的接口,但您(尚未)准备好为所有具体情况提供实现。请注意,这应被视为违反Liskov Substitution Principle;然而,如果有一个 * 意图 * 支持未来的实现,这可能是一个值得的权衡。作为结论,显然有些人使用
ArgumentOutOfRangeException
,也提到了by one of the existing answers。但是,这让我感到奇怪:文件中可能相关的唯一指示是:* “包含无效值,该值不是参数”*“所需的值集的成员。但是,(1)如果这是标准,那么基本上任何
ArgumentException
都可以被认为是范围异常,使类型毫无意义;和(2)类型名称中的“range”意味着 * 可比 * 值,这在语义上不是枚举。