2011-07-11 35 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產生的可能位掩碼相符。 –