这是我的other question about unhandled cases with enums的另一个案例,我建议将其作为一个单独的问题提出。
假设我们有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());
}
}
如您所见,我们处理所有可能的枚举值但仍保留默认值,以防添加新成员,并且我们希望确保我们知道丢失的处理。
我的问题是:在您想要通知未处理/实施给定代码路径或从未访问过的情况下,什么是正确的异常?我们曾经使用NotImplementedException
,但它似乎不合适。我们的下一位候选人是InvalidOperationException
,但这个词听起来不对。什么是正确的,为什么?
答案 0 :(得分:27)
我个人在项目中添加了一个自定义异常:
public class UnexpectedEnumValueException<T> : 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<SomeEnum>(value);
}
}
这样我就可以搜索&#34; UnexpectedEnumValueException&lt; SomeEnum&gt;&#34;例如,当我向SomeEnum添加新值时,我想找到可能受更改影响的所有位置。错误消息比通用异常更清晰。
答案 1 :(得分:23)
由于内部操作失败(产生无效的内容),InvalidOperationException
是可行的方法。
文档只是说:
当方法调用对象的当前状态无效时引发的异常。
大致是合适的,因为对象的当前状态导致someOtherFunc
的返回值无效,因此首先应该避免调用someFunc
。
答案 2 :(得分:20)
尝试使用InvalidEnumArgumentException Class
void someFunc()
{
SomeEnum value = someOtherFunc();
switch(value)
{
case One:
... break;
case Two:
... break;
default:
throw new InvalidEnumArgumentException();
}
}
答案 3 :(得分:8)
我认为这取决于枚举所代表的语义。
如果InvalidOperationException表示对象状态,则它是合适的。
如果NotSupportedException表示不受支持的应用程序功能,则
{{3}}适用于当前未实现但可能在将来版本中的应用程序功能。
...
答案 4 :(得分:4)
开关案例的Resharper主张:
switch(parameter)
{
default:
throw new ArgumentOutOfRangeException("parameter");
}
但它无法满足您的需求。如果没有,您可以定义关于此函数中执行的内容的自定义异常类型:SomeEnumOutOfRangeException ...
答案 5 :(得分:2)
如果添加了新值并且您忘记在某处处理它,则会出现编程错误,或者Eric Lippert称之为Boneheaded Exception。我创建了自己的BoneheadedException
类,每当我检测到编程错误时,我就抛出这个类,而没有更合适的FCL异常类型。
答案 6 :(得分:1)
在这种情况下我尝试做的是使用字典而不是switch
语句。 (一般来说,这样的映射可以通过字典更好地定义;我几乎认为switch
语句会自动编写代码,因为总是或几乎总是有更好的方法来组织这样的映射。)
如果您使用字典,那么如果您尝试使用尚未计算的值对字典编制索引,您将获得KeyNotFoundException
,并且不再有理由问“做什么”我在默认情况下做什么?“
答案 7 :(得分:0)
这种情况实际上是合同失败,并不一定特定于枚举。如果你不能使用代码契约,你可以创建自己的异常类型并抛出它。
case One:
... break;
case Two:
... break;
default:
throw new ContractViolationException("Invalid enum");
答案 8 :(得分:0)
这几天,我编写了两个自定义例外:UnexpectedValueException
和UnexpectedTypeException
。在我看来,这些似乎是非常有价值的异常,因为一旦您确定出现了意外的(假定不存在)值或类型,就应该尽可能多地传达有意义的消息。
class UnexpectedValueException : Exception {
public UnexpectedValueException(object pValue)
: base($"The value '{pValue}' of type '{pValue?.GetType().ToString() ?? "<null>"}' 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);
}
}
答案 9 :(得分:0)
我确定要使用的是ArgumentOutOfRangeException
,它带有一个自定义错误消息,该错误消息的灵感来自默认消息InvalidEnumArgumentException
(由于它位于System.ComponentModel内,所以我不愿使用它):
参数'{nameof(theArgument)}'({theArgument})的值对于枚举类型'{nameof(TheEnumType)}'无效。
这是它的样子:
return level switch
{
LogEventLevel.Verbose => "VERBOSE",
LogEventLevel.Debug => "DEBUG",
LogEventLevel.Information => "INFO",
LogEventLevel.Warning => "WARN",
LogEventLevel.Error => "ERROR",
LogEventLevel.Fatal => "FATAL",
_ => throw new ArgumentOutOfRangeException(nameof(level), level,
$"The value of argument '{nameof(level)}' ({level}) is invalid for enum type '{nameof(LogEventLevel)}'.")
};
答案 10 :(得分:-2)
我会说NotImplementedException,因为你为未实现的枚举值抛出异常。