一个选项是在调试模式下进行几乎方法契约检查。为美观的表单添加一个扩展方法:
[Conditional("DEBUG")]
public static bool AssertIsValid(this System.Enum value)
{
if (!System.Enum.IsDefined(value.GetType(), value))
throw new EnumerationValueNotSupportedException(value.GetType(), value); //custom exception
}
我认为它可能只处于调试模式,因此它可以通过您的开发/测试环境和单元测试,并且在生产中没有开销(尽管这取决于您)
public string GetCallingCode(Guid countryId)
{
var country = GetCountry(countryId);
country.AssertIsValid(); //throws if the country is not defined
switch (country)
{
case Country.UnitedStates:
return "1";
case Country.Mexico:
return "52";
}
}
我建议这实际上是您的 GetCountry 方法的责任。 它应该识别countryId无效并抛出异常。
无论如何,这也应该被您的单元测试捕捉到,或者以某种方式更好地处理。无论您在何处将字符串/int 转换为枚举,都应该由一个单一的方法处理,该方法反过来可以检查/抛出(就像任何 Parse 方法一样)并且有一个检查所有有效数字的单元测试。
总的来说,我不认为各种ArgumentExceptions(等)是一个好的候选者,因为有几个条件(非参数)情况。我认为如果您将检查代码移动到一个位置,您不妨抛出自己的异常,该异常可以准确地传达给任何正在监听的开发人员。
编辑:考虑到讨论,我认为这里有两个特殊情况。
案例 1:将底层类型转换为等效枚举
如果您的方法采用某种类型的输入数据(字符串、整数、Guid?),您执行转换为枚举的代码应该验证您有一个可用的实际 枚举。这是我在上面的答案中发布的情况。在这种情况下,可能会抛出您自己的异常或InvalidEnumArgumentException。
这应该像任何标准输入验证一样对待。在您系统的某个地方,您提供了垃圾输入,因此请像处理任何其他解析机制一样处理它。
var country = GetCountry(countryId);
switch (country)
{
case Country.UnitedStates:
return "1";
case Country.Mexico:
return "52";
}
private Country GetCountry(Guid countryId)
{
//get country by ID
if (couldNotFindCountry)
throw new EnumerationValueNotSupportedException(.... // or InvalidEnumArgumentException
return parsedCountry;
}
编辑:当然,编译器要求你的方法抛出/返回,所以不太确定你应该在这里做什么。我想这取决于你。如果确实发生了这种情况,那可能是一个愚蠢的异常(下面的案例 2),因为您通过了输入验证然而没有更新开关/案例来处理新值,所以也许它应该 @ 987654331@;
案例 2:添加一个新的枚举值,您的代码中的 switch/case 块没有处理该值
如果您是代码的所有者,这属于 Eric Lippert 在@NominSim 的回答中描述的“Boneheaded”异常。虽然这实际上不会导致异常同时使程序处于异常/无效状态。
最好的方法可能是在您执行 switch/case(或类似操作)的任何地方针对枚举运行,您应该考虑编写一个单元测试,自动针对 运行该方法>所有定义的枚举值。因此,如果您懒惰或不小心错过了某个块,您的单元测试会警告您没有更新方法来说明枚举列表中的更改。
最后,如果您的枚举来自您没有意识到他们更新了值的第 3 方,您应该编写一个快速的单元测试来验证您的所有预期值。因此,如果您在编写程序时检查了UnitedStates 和Mexico,那么您的单元测试应该只是这些值的开关/案例块并抛出异常,否则会在/如果它们最终添加Canada 时警告您。当更新第 3 方库后测试失败时,您就知道必须在哪些/在哪里进行更改才能兼容。
所以在这个“案例 2”中,您应该抛出任何您想要的旧异常,因为只要它准确地与 您 或您的单元测试的消费者沟通,它就会由您的单元测试处理正是缺少的东西。
在任何一种情况下,我都不认为 switch/case 代码应该过多地关心无效输入并且不在那里抛出异常。它们应该在设计时抛出(通过单元测试)或在验证/解析输入时抛出(因此抛出适当的“解析/验证”异常)
编辑:我偶然发现post from Eric Lippert 讨论 C# 编译器如何检测具有返回值的方法是否在没有返回的情况下达到其“终点”。编译器擅长保证端点有时是不可达的,但在上述情况下,我们开发人员知道它是不可达的(除了上面提到的BoneheadedExceptions 发挥作用的情况)。
没有讨论过(至少我看到了)作为开发人员应该做些什么来解决这些情况。编译器要求您提供返回值或抛出异常,即使您知道它永远不会到达该代码。在这种情况下,谷歌搜索并没有神奇地出现一些可以利用的异常(尽管我无法找出好的搜索词),我宁愿抛出一个异常并被告知我的假设它不能到达终点是不正确的,而不是返回 一些值,这可能不会通知我问题或导致不需要的行为。也许某种UnexpectedCodePathFailedToReturnValueException 在这种情况下最有效。当我有时间时,我会做更多的挖掘工作,也许会在programmers 上发布一个问题以引起一些讨论。