抛出未处理的枚举值的正确exception是什么?
这是我关于未处理的枚举案件的另一个问题的另一个案例,我建议将其作为一个单独的问题提出。
假设我们有SomeEnum
并且有一个switch语句来处理它:
enum SomeEnum { One, Two } void someFunc() { SomeEnum value = someOtherFunc(); switch(value) { case One: ... break; case Two: ... break; default: throw new ??????Exception("Unhandled value: " + value.ToString()); } }
如您所见,我们处理所有可能的枚举值,但仍保留默认值,以防添加新成员,并且我们希望确保我们知道丢失的处理。
我的问题是:在你想要通知给定的代码路径没有被处理/实现或者从未被访问过的情况下,什么是正确的exception? 我们曾经使用过NotImplementedException
但它似乎不合适。 我们的下一个候选者是InvalidOperationException
但该术语听起来不正确。 什么是正确的,为什么?
由于它是一个失败的内部操作(产生一些无效的东西), InvalidOperationException
是要走的路。
文档简单地说:
方法调用对于对象的当前状态无效时引发的exception。
这大致是合适的,因为对象的当前状态导致someOtherFunc
的无效返回值,因此someFunc
应该避免调用someFunc
。
我个人在项目中添加了一个自定义exception:
public class UnexpectedEnumValueException : Exception { public UnexpectedEnumValueException( T value ) : base( "Value " + value + " of enum " + typeof( T ).Name + " is not supported" ) { } }
然后我可以根据需要选择它:
enum SomeEnum { One, Two } void someFunc() { SomeEnum value = someOtherFunc(); switch(value) { case SomeEnum.One: ... break; case SomeEnum.Two: ... break; default: throw new UnexpectedEnumValueException(value); } }
这样我可以搜索“UnexpectedEnumValueException
尝试使用InvalidEnumArgumentException类
void someFunc() { SomeEnum value = someOtherFunc(); switch(value) { case One: ... break; case Two: ... break; default: throw new InvalidEnumArgumentException(); } }
我认为这取决于枚举所代表的语义。
如果InvalidOperationException表示对象状态,则它是合适的。
如果NotSupportedException表示不受支持的应用程序function,则它是合适的。
NotImplementedException适用于当前未实现但可能在将来版本中的应用程序function。
…
关于开关案例的Resharper主张:
switch(parameter) { default: throw new ArgumentOutOfRangeException("parameter"); }
但它无法满足您的需求。 如果没有,您可以定义关于此函数中执行的内容的自定义exception类型:SomeEnumOutOfRangeException …
如果添加了一个新值而你忘了在某个地方处理它,那就是编程错误,或者是Eric Lippert称之为的Boneheaded Exception 。 我创建了自己的BoneheadedException
类,每当我检测到编程错误时,我就会抛出这个类,而没有更合适的FCLexception类型。
我在这种情况下尝试做的是使用字典而不是switch
语句。 (通常,像这样的映射更好地由字典定义;我几乎认为switch
语句会自动代码气味,因为总是或几乎总是有更好的方法来组织这样的映射。)
如果您使用字典,那么如果您尝试使用尚未计算的值索引字典,您将获得KeyNotFoundException
,并且不再有理由询问“在默认情况下我该怎么办? ”。
这种情况实际上是合同失败,并不一定特定于枚举。 如果你不能使用代码契约,你可以创建自己的exception类型并抛出它。
case One: ... break; case Two: ... break; default: throw new ContractViolationException("Invalid enum");
这些天我写了两个自定义exception: UnexpectedValueException
和UnexpectedTypeException
。 在我看来,这些非常有价值的例外,因为一旦你发现了一个意外的(意味着假设不存在)值或类型,你应该尽可能地使用尽可能有意义的消息。
class UnexpectedValueException : Exception { public UnexpectedValueException(object pValue) : base($"The value '{pValue}' of type '{pValue?.GetType().ToString() ?? ""}' was not expected to exist.") { } } enum SomeEnum { One, Two, Three } void someFunc() { SomeEnum value = someOtherFunc(); switch (value) { case One: ... break; case Two: ... break; default: throw new UnexpectedValueException(value); } }
我会说NotImplementedException,因为你为未实现的枚举值抛出exception。