我的团队最近开始使用Lance Hunt的C#编码标准文档作为巩固我们的编码标准的起点.
有一个项目,我们只是不明白的意义,这里的任何人都可以阐明它吗?
该项目是第77号:
在使用之前始终验证枚举变量或参数值.它们可以包含底层Enum类型(默认int)支持的任何值.
例:
public void Test(BookCategory cat) { if (Enum.IsDefined(typeof(BookCategory), cat)) {…} }
Jon Skeet.. 20
关键是你可能希望通过拥有BookCategory类型的参数,你总是有一个有意义的书类别.情况并非如此.我可以打电话:
BookCategory weirdCategory = (BookCategory) 123456; Test(weirdCategory);
如果枚举意味着代表一组众所周知的值,则不应期望代码合理地处理该知名集合之外的值.测试首先检查参数是否合适.
我个人反过来说:
public void Test(BookCategory cat) { if (!Enum.IsDefined(typeof(BookCategory), cat)) { throw new ArgumentOutOfRangeException("cat"); } }
在C#3中,可以使用扩展方法轻松完成:
// Can't constrain T to be an enum, unfortunately. This will have to do :) public static void ThrowIfNotDefined(this T value, string name) where T : struct { if (!Enum.IsDefined(typeof(T), value)) { throw new ArgumentOutOfRangeException(name); } }
用法:
public void Test(BookCategory cat) { cat.ThrowIfNotDefined("cat"); }
请参阅http://blogs.msdn.com/brada/archive/2003/11/29/50903.aspx,了解不应使用Enum.IsDefined的原因. (4认同)
仿制药不允许"Enum"/"enum"作为约束,这是一种痛苦...... (2认同)
Marc Gravell.. 17
未检查枚举:
enum Foo { A= 1, B = 2, C = 3} Foo x = (Foo) 27; // works fine
现在你有一个Foo
未定义的.他只是说"检查你的输入".请注意,Enum.IsDefined
它相对较慢(基于反射).就个人而言,我倾向于使用带有default
抛出异常的开关:
switch(x) { case Foo.A: /* do something */ break; case Foo.B: /* do something */ break; case Foo.C: /* do something */ break; default: throw new ArgumentOutOfRangeException("x"); }
可以说,NotSupportedException可能更合适 - 但是哎呀,只要它抛出! (2认同)
同意!抛出新的OldShoeException()比让逻辑失败以及因此出现在其他地方的bug更好. (2认同)
Lance Hunt.. 13
我认为上面的评论几乎都回答了这个问题.基本上,当我写这个规则时,我试图传达一种验证所有输入的防御性编码实践.枚举是一种特殊情况,因为许多开发人员错误地认为它们是经过验证的,而不是.因此,您经常会看到语句或switch语句是否因未定义的枚举值而失败.
请记住,默认情况下,枚举只不过是INT的包装器,并且就像它是一个int一样验证它.
有关正确使用枚举的更详细讨论,您可以查看Brad Abrams和Krzysztof Cwalina的博客文章或他们的优秀书籍"框架设计指南:可重用.NET库的约定,惯用法和模式"
关键是你可能希望通过拥有BookCategory类型的参数,你总是有一个有意义的书类别.情况并非如此.我可以打电话:
BookCategory weirdCategory = (BookCategory) 123456; Test(weirdCategory);
如果枚举意味着代表一组众所周知的值,则不应期望代码合理地处理该知名集合之外的值.测试首先检查参数是否合适.
我个人反过来说:
public void Test(BookCategory cat) { if (!Enum.IsDefined(typeof(BookCategory), cat)) { throw new ArgumentOutOfRangeException("cat"); } }
在C#3中,可以使用扩展方法轻松完成:
// Can't constrain T to be an enum, unfortunately. This will have to do :) public static void ThrowIfNotDefined(this T value, string name) where T : struct { if (!Enum.IsDefined(typeof(T), value)) { throw new ArgumentOutOfRangeException(name); } }
用法:
public void Test(BookCategory cat) { cat.ThrowIfNotDefined("cat"); }
未检查枚举:
enum Foo { A= 1, B = 2, C = 3} Foo x = (Foo) 27; // works fine
现在你有一个Foo
未定义的.他只是说"检查你的输入".请注意,Enum.IsDefined
它相对较慢(基于反射).就个人而言,我倾向于使用带有default
抛出异常的开关:
switch(x) { case Foo.A: /* do something */ break; case Foo.B: /* do something */ break; case Foo.C: /* do something */ break; default: throw new ArgumentOutOfRangeException("x"); }
我认为上面的评论几乎都回答了这个问题.基本上,当我写这个规则时,我试图传达一种验证所有输入的防御性编码实践.枚举是一种特殊情况,因为许多开发人员错误地认为它们是经过验证的,而不是.因此,您经常会看到语句或switch语句是否因未定义的枚举值而失败.
请记住,默认情况下,枚举只不过是INT的包装器,并且就像它是一个int一样验证它.
有关正确使用枚举的更详细讨论,您可以查看Brad Abrams和Krzysztof Cwalina的博客文章或他们的优秀书籍"框架设计指南:可重用.NET库的约定,惯用法和模式"