C# 当一个未知值被传递到switch语句中时,我应该引发什么类型的异常 编辑1
已更新以使枚举不是方法的参数 问题: switch语句中的枚举经常出现这种问题。在示例代码中,开发人员说明了程序当前使用的所有国家,但如果将另一个国家添加到国家枚举,则应引发异常。我的问题是,应该抛出什么类型的异常 示例代码: 我看过C# 当一个未知值被传递到switch语句中时,我应该引发什么类型的异常 编辑1,c#,exception-handling,C#,Exception Handling,已更新以使枚举不是方法的参数 问题: switch语句中的枚举经常出现这种问题。在示例代码中,开发人员说明了程序当前使用的所有国家,但如果将另一个国家添加到国家枚举,则应引发异常。我的问题是,应该抛出什么类型的异常 示例代码: 我看过 ,当请求的方法或操作未实现时引发的异常 有些方法在基类中不受支持,期望这些方法将在派生类中实现。派生类可能只实现基类中方法的一个子集,并对不受支持的方法抛出NotSupportedException。 对于对象有时可能执行请求的操作,并且对象状态决定是否可以执行
- ,当请求的方法或操作未实现时引发的异常
- 有些方法在基类中不受支持,期望这些方法将在派生类中实现。派生类可能只实现基类中方法的一个子集,并对不受支持的方法抛出NotSupportedException。
对于对象有时可能执行请求的操作,并且对象状态决定是否可以执行该操作的场景,请参阅InvalidOperationException - 在调用方法失败是由无效参数以外的原因引起的情况下使用
我猜不是没有实现就是操作无效。我应该用哪一个?有人有更好的选择吗(我知道滚动你自己的总是一个选择)我会选择
ArgumentException
,因为agrument是无效的
编辑:
还有
InvalidEnumArgumentException
,它可能更准确地描述了这个问题,但是,我以前从未见过有人使用过它。不可能传递另一个值,因为枚举将可能的值限制为您处理的值。所以您不需要任何异常。在您列出的异常中,只有InvalidOperationException
适合您的场景。我会考虑使用这一点或更具体的,因为您的代码>开关>代码>值作为参数提供。
或者,就像你说的,自己滚
编辑:根据您更新的问题,如果您想使用框架异常,我建议您使用
InvalidOperationException
。但是,对于这种更一般的情况,我当然更愿意使用我自己的异常类型-您不能保证调用堆栈中的其他地方不会捕获到InvalidOperationException
(可能是框架本身!),因此使用您自己的异常类型更为健壮。就我个人而言,我不认为这是一个适合任何例外的地方。如果添加国家/地区,则应在switch语句中添加案例。代码不应该因为向枚举添加了值而中断
Eric Lippert有一篇关于何时使用异常的文章,它将您正在寻找的异常类型分类为:(请原谅这不是我的措辞)
bonehead异常是您自己的错误,您本可以阻止它们,因此它们是代码中的错误<你不应该抓住他们;这样做会在代码中隐藏一个bug。相反,您应该编写代码,使异常首先不可能发生,因此不需要捕获
一个选项是在调试模式下执行几乎一个方法契约检查。为美观的表单添加扩展方法:
[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
}
我想它可能只有在调试模式下才能通过开发/测试环境和单元测试,在生产环境中没有开销(尽管这取决于您)
我建议这实际上是您的GetCountry
方法的责任。它应该识别countryId
无效,并引发异常
无论如何,这也应该被单元测试捕获,或者以某种方式更好地处理。无论在何处将字符串/int转换为enum,都应该由一个单数方法处理,该方法可以检查/抛出(就像任何Parse
方法一样),并有一个检查所有有效数字的单元测试
一般来说,我不认为各种参数异常(以及类似情况)是一个很好的候选,因为有几个条件(非参数)情况。我认为,如果您将检查代码移动到一个位置,您还可以抛出自己的异常,该异常可以准确地与任何正在侦听的开发人员进行通信
编辑:考虑到讨论,我认为这里有两个特殊的案例
案例1:将基础类型转换为等效枚举
如果您的方法采用某种类型的输入数据(字符串、int、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;
}
编辑:当然,编译器要求您的方法抛出/返回,所以不太确定您应该在这里做什么。我想这取决于你。如果真的发生了这种情况,那么它可能是一个BoneHeadeException异常(下面的案例2),因为您通过了输入验证,但没有更新开关/案例来处理新值,所以它可能应该抛出新的BoneHeadeException()
案例2:添加代码中开关/案例块未处理的新枚举值
如果您是代码的所有者,这属于Eric Lippert在@NominSim的回答中描述的“骨头”异常。尽管这实际上根本不会导致异常,同时使程序处于异常/无效状态
最好的是,在任何一个执行开关的情况下(或类似的情况)都是针对枚举运行的,您应该考虑编写一个单元TE。
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";
}
}
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;
}