2011-07-11 34 views
4

当阅读Jon Skeet对这个特定问题的回答How can I make it so my class variables can only be set to one of three choices?我学到了一些新的东西,我不知道在C#中,我猜测CLR类型系统。为什么即使在枚举中没有定义有效值时也允许使用枚举转换

这是什么是合法的理由:

public enum Tests 
{ 
    Zero = 0, 
    One, 
    Two 
} 

var nonExistantTestsValue = (Tests)1000; 

这将编译完全没有问题,但我不明白为什么它应该是这样的原因。就我所能看到的而言,枚举的所有要点是将特定类型的给定变量的值限制为一定数量的选项。

如果enum定义所强加的限制很容易被破坏,那么除了可读性问题之外,还有什么意义?你显然可以确定它是一个使用反射的有效值,但为什么这不是在编译时完成的?

这可能是一个很好的理由,它的工作方式,但我看不到它。

+0

[?为什么铸造INT无效枚举值不会抛出异常]的可能重复(http://stackoverflow.com/questions/6413804/why-母鹿s-cast-int-to-invalid-enum-value-not-throw-exception) – nawfal

回答

1

它可以使用任何值,因为您可以用“Flags”属性标记枚举。这意味着您可以通过对枚举的各种成员进行OR值来组合任何值。 基本上,编译器不够智能,无法照顾使用枚举的任何可能方式。

编辑:发现了一个以前的帖子由埃里克利珀:

Why does casting int to invalid enum value NOT throw exception?

+0

谢谢!有趣的是,我觉得我的感觉和Eric所做的一样,那就是拥有一套机制来确保选项的凝聚。为什么枚举的行为似乎是这样的,或者Eric声称(这对我来说已经足够了),枚举可以用作标志。 – InBetween

+0

@InBetween:cohesion必须是_ [cohesion](http://en.wikipedia.org/wiki/Cohesion)_和_ [胁迫](http://en.wikipedia.org/wiki/Coercion)的婚姻。 _, 然后。无论如何,Eric Lippert是这方面的权威人士。 – sehe

+0

@sehe:大声笑,我应该坚持的话我真的知道如何写:页。冒着时不是你的母语通常不是一个好主意...... – InBetween

3

如果由枚举定义所施加的限制是那么容易坏,有什么意义

我觉得枚举抽象不是设计与限制保证记在心里。我认为它的设计采用了方便可维护性记住

好原因:

跳过第一颗子弹,如果你不希望看到简单的真理分辩现在

  • 语言规范[我提到这个的原因是为了提醒人们有关辩论事实的有限使用;像... then what's the point短语触发这对我来说]

  • 性能(其硬/不可能完成的验证将不需要,这将在一个大的方式进行阻碍的表现时说特定的应用程序

(记住CLR函数可以从任何地方调用,不仅仅是C#,而不仅仅是您的程序集)

+0

感谢您的回答。 1)是的,否则这将是一个善意的错误。我想到了一个。 2)是的,我明白这可能是这种情况,但为什么它首先允许(而不是c#)?这可能是一个简单的编译时检查,没有任何语言成本。 – InBetween

+0

最后一句+1。我认为OP不理解铸造是危险的,应该避免。 –

+1

调用语言规范的原因是循环论证。 – CodesInChaos

1

我不知道的原因,这个设计决定的,但我们可以看看它的一些后果。

让我们看一个枚举的IL表示:

.class private auto ansi sealed MyEnum 
    extends [mscorlib]System.Enum 
{ 
    // Fields 
    .field public specialname rtspecialname int32 value__ 
    .field public static literal valuetype MyEnum Value0 = int32(0) 
    .field public static literal valuetype MyEnum Value1 = int32(1) 
    .field public static literal valuetype MyEnum Value2 = int32(2) 

} 

首先,我们注意到,它是值类型,因此(MyEnum)0必须是有效的。其次,我们看到枚举的可能值仅为const s,并且运行时级别的枚举与赋值兼容的整数文字。

枚举常量通常变成一个整数文字。所以,如果你想防止出现无效的枚举,你需要在从枚举转换时引入昂贵的运行时检查,或者不平凡的跨装配加载时间检查,以确保烘焙到另一个装配中的枚举文字是有效的。


另一件事是,有可能创建长支持的枚举。但长期的一个特点是他们的任务不能保证是原子的。所以保证基于枚举的long的值是有效的。

enum MyLongEnum:long 
{ 
    Value1=0x0101010102020202, 
    Value2=0x0303030304040404 
} 

如果分配给从多个线程这样的枚举,你可以用混合值是即使你从来没有指定的值无效无效告终。


也有一个简单的解决办法,以获得安全的枚举:使用一类具有私有构造和静态只读字段或属性的可能值。这样你就失去了整数转换,文字和非空性,但增益类型安全和更好的版本控制。

+0

谢谢!好点。而确保选项限制的静态类的想法似乎足够好。这是我对可标记的枚举行为的错误想法。 – InBetween

5

Enumerations本质上是唯一的类型,允许您将符号名称分配给整数值。它们不是用于限制变量的允许值...

+1

我想我对枚举的理解是离开的。我现在明白它为什么会这样,特别是它们被用作标志。尽管如此,我认为在类型系统中有一个很好的选择,可以通过某种枚举来强加选项限制(如果可能或通过其他方式通过属性)。这意味着每次我有一个不打算用作标志的枚举参数时,我必须检查它是否有效。你在使用枚举的可读性方面赢得了什么,你在代码混乱中也会部分松动,从而验证它们。 – InBetween

+0

@InBetween:那么这将违背便利,这正是@sehe正在取得的。 – 2011-07-11 13:12:00

+0

@ 0A0D:是的,我现在明白了。这就是为什么我只是在思考一个能够做到这一点的选择,以防它有一些好处。无论如何,它不是所有已经发布在这里的好理由的选择。 – InBetween

2

这是你可以做什么时,枚举不限于有值的更多质疑:

标志是其中一个例子:

[Flags] 
enum MyFlag 
{ 
    a, 
    b, 
    c 
} 

现在你可以做位操作:

MyFlag flags = MyFlag.a|MyFlag.b; 
+2

+1标志组合的好处。 – sehe

+0

这是一个非常好的理由,谢谢。这个答案和同伴们是谁低调的?!? – InBetween

+0

[Flags]将enum的语义改为编译器 - 如果枚举是[flag] ged,那么编译器应该发出运行时检查以验证转换后的值是否与a,b和c产生的可能位掩码相符。 –