抛出未处理的枚举值的正确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 ”,例如,我向SomeEnum添加新值,我想找到可能受更改影响的所有地方。 错误消息比通用exception更清晰。

尝试使用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: UnexpectedValueExceptionUnexpectedTypeException 。 在我看来,这些非常有价值的例外,因为一旦你发现了一个意外的(意味着假设不存在)值或类型,你应该尽可能地使用尽可能有意义的消息。

 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。