43

这是我关于未处理的枚举案例的另一个问题的另一个案例,建议我将其作为一个单独的问题提出。

假设我们有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,但这个词听起来不对。什么是正确的,为什么?

4

11 回答 11

38

就个人而言,我为我的项目添加了一个自定义异常:

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);    
  }
}

这样,我可以搜索“UnexpectedEnumValueException<SomeEnum>”,例如,当我向 SomeEnum 添加新值并且我想找到所有可能受更改影响的地方时。错误消息比一般异常要清楚得多。

于 2014-07-15T15:54:37.633 回答
31

因为它是一个失败的内部操作(产生无效的东西),InvalidOperationException所以是要走的路。

文档只是说:

当方法调用对于对象的当前状态无效时引发的异常。

这大致合适,因为对象的当前状态导致 的返回值无效someOtherFunc,因此someFunc应该首先避免调用 。

于 2012-11-30T12:30:45.417 回答
26

尝试使用InvalidEnumArgumentException 类

void someFunc()
{
  SomeEnum value = someOtherFunc();
  switch(value)
  {
     case One:
       ... break;
     case Two:
       ... break;
     default:
          throw new InvalidEnumArgumentException(); 
  }
}
于 2012-11-30T12:25:34.263 回答
11

我认为这取决于枚举所代表的语义。

如果InvalidOperationException表示对象状态,则它是合适的。

如果NotSupportedException表示不受支持的应用程序功能,则它是合适的。

NotImplementedException适用于当前未实现但可能在未来版本中的应用程序功能。

...

于 2012-11-30T12:30:12.050 回答
5

开关盒的 ReSharper 命题:

switch(parameter)
{
   default:
      throw new ArgumentOutOfRangeException("parameter");
}

但它可能不适合您的需求,在这种情况下,您可以定义一个自定义异常类型,了解在此函数中执行的操作:SomeEnumOutOfRangeException...

于 2012-11-30T12:30:35.790 回答
2

如果添加了一个新值而您忘记在某处处理它,则这是一个编程错误,或者是Eric Lippert 所说的Boneheaded Exception 。我创建自己的BoneheadedException类,每当我检测到没有 FCL 异常类型更适合的编程错误时,我都会抛出该类。

于 2012-11-30T12:37:39.420 回答
1

在这种情况下,我尝试做的是使用字典而不是switch语句。(一般来说,像这样的映射由字典更好地定义;我几乎认为switch语句是自动的代码气味,因为总是或几乎总是有更好的方法来组织这样的映射。)

如果您使用字典,那么如果您尝试使用尚未考虑的值来索引您的字典,您将得到一个KeyNotFoundException,并且不再有理由问“在默认情况下我该怎么做?” .

于 2017-04-12T21:06:03.910 回答
-1

这种情况实际上是合同失败,不一定特定于枚举。如果你不能使用代码契约,你可以创建自己的异常类型并抛出它。

case One:
   ... break;
 case Two:
   ... break;
 default:
    throw new ContractViolationException("Invalid enum");
于 2012-11-30T12:33:58.450 回答
-1

这些天我写了两个自定义异常:UnexpectedValueExceptionUnexpectedTypeException. 在我看来,这些似乎是非常有价值的例外,因为一旦您发现意外(即假定不存在)值或类型的发生,您应该尽可能地抛出有意义的消息。

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);    
   }
}
于 2018-09-27T18:01:02.383 回答
-1

我决定使用的是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)}'.")
};
于 2020-11-10T15:21:36.143 回答
-2

我会说 NotImplementedException 因为您正在为未实现的枚举值抛出异常。

于 2012-11-30T13:03:20.127 回答