2016-04-05 94 views
6

Total number of items defined in an enum,我看我可以用得到枚舉的數量:如何獲取常量的枚舉數?

Enum.GetNames(typeof(Item.Type)).Length; 

的偉大工程!

但是,我需要這個數字作爲常數,以便我可以在Unity的[Range(int, int)]功能中使用它。

private const int constEnumCount = Enum.GetNames(typeof(Item.Type)).Length; 

上述不起作用,因爲枚舉數不是一個常數,所以我不能將它賦值給一個常量變量。

如何獲得常量的枚舉數?

+1

將此定義爲'const'時應該小心,因爲該值在編譯時需要,並且編譯時的值將在所有引用的程序集中傳播。是否可以使用只讀? –

+0

@JoeBlow這是開發人員爲了進行遊戲測試而可以擺動數字的範圍。它用於編輯引擎,以便在運行前和運行期間對代碼進行更改。 – Evorlor

+1

你是指編輯器檢查器屬性!很像[Header(「Hi there」)]。不,你絕對不能以任何方式改變它。 – Fattie

回答

9

不可能將枚舉數作爲constEnum.GetNames使用反射來獲得這些東西,這本質上是一個運行時操作。因此,在編譯時無法知道,這是const的要求。

1

您不能在運行時分配/創建const。這就是你想要做的。 A const必須是fully evaluated at compile time,而不是運行時。

我不知道團結(以及爲什麼它需要一個const,但我會尋找使用readonly

private readonly int constEnumCount = Enum.GetNames(typeof(Item.Type)).Length; 
+0

不幸的是,Unity不會接受只讀。我想我應該問一個問題,爲什麼在編譯時不能計算枚舉的長度。 – Evorlor

+1

作者嘗試使用此值作爲屬性參數,這就是爲什麼它需要const – Grundy

2

不幸的是,這是不可能以任何有意義的方式做由於技術限制:

  • [Range(int,int)]是一個屬性,並提供給一個屬性的全部信息都是一個const
  • 鄰只有使用Enum.GetValues(typeof(MyEnum)).LengthEnum.GetNames(typeof(MyEnum)).Length才能獲得枚舉值的數目,這兩者都是運行時反射。

但是,有些黑客可以進行排序工作。例如,枚舉的值可以轉換爲整數。只要沒有人明確地定義你的枚舉值,你可以在你的枚舉使用最後一個元素有點像這樣:

[Range(0,(int)MyEnum.LastValue)] 
public void BlahBlahBlah() {} 

然而,知道只要有人一前一後增加了一個新條目枚舉您正在使用或重新排列枚舉中的元素,您的代碼將不可預知地中斷並且不像您想要的那樣行爲。

這很令人傷心,但是C#編譯器不夠聰明,無法在Java,C和C++編譯器等編譯器中進行簡單的數學運算。所以即使我給出的例子只會在LastValue以前用於除了標記枚舉中最後一個元素以外的任何東西。它降低了C#編譯器的複雜性,這也極大地提高了您的應用程序的編譯速度。因此,C#和CLR團隊爲改善您在其他地方的體驗而進行了一些權衡。

+0

這與C#編譯器不夠聰明無關。 'const'被定義爲_constant_。計算的值不是:如果枚舉(例如,在另一個程序集中定義的)突然具有更多或更少的值,則計算的值將需要更改。這就是爲什麼該值需要在運行時計算的原因。 –

+0

@DanielRose,與C或Java相比,C#無法評估(在寫答案的時候)'const int myval = 3 + 5 + 8;'即使答案是_constant_。這就是我所指的。某些語言將在初始化時評估常量。您可以使用「靜態只讀」字段,但不能使用類/方法/參數屬性。 –

2

假設您需要const,因爲您試圖指定屬性的值(this one?),那麼您運氣不好。

的選項有:

  1. 硬編碼在屬性聲明或作爲const本身的數量和小心,以保持計數同步與enum定義
  2. 使用PostSharp或一些其他方面的導向框架爲你插入屬性。從來沒有做過這樣自己,但它看起來可能(How to inject an attribute using a PostSharp attribute?

你也許還欺騙了一些方法來使這個與T4模板,但會得到過度缺憾。

如果是我,除非你說的是數百個不同的枚舉集合,否則我會硬編碼長度並可能在需要的地方添加一個Assert。

// WARNING - if you add members update impacted Range attributes 
public enum MyEnum 
{ 
    foo, 
    bar, 
    baz 
} 

[Range(0, 3)] 
class Test 
{ 
    public Test() 
    { 
     int enumCount = Enum.GetNames(typeof(MyEnum)).Length; 
     int rangeMax = GetType().GetCustomAttributes(typeof(Range), false).OfType<Range>().First().Max; 

     Debug.Assert(enumCount == rangeMax); 
    } 
    static void Main(string[] args) 
    { 
     var test = new Test(); 
    } 
} 
0

在C#,一個const被定義爲常數,即,值(未其中產生它的計算!)直接嵌入在編譯的程序集。

爲了阻止您做某些問題,編譯器不允許您將計算結果分配給常量。要理解爲什麼,想象它是可能的:

A.dll 
    public enum Foo { A, B } 

B.dll 
    public const NumberOfFoos = Enum.GetNames(typeof(Foo)).Length; 

// Compiles to: 
B.dll 
    public const NumberOfFoos = 2; 

現在你改變A.DLL:

A.dll 
    public enum Foo { A, B, C, D } 

B.dll 
     public const NumberOfFoos = 2; // Oh no! 

所以,你會需要重新編譯B.DLL以得到正確的值。

它變得更糟:想象一下你有一個程序集C.dll,它使用B.NumberOfFoos。值2將直接嵌入到C.dll中,因此也將需要重新編譯, B.dll,才能得到正確的值。因此(成功的坑),C#不允許你對const進行賦值。

+0

這與將「public const int x = 5;'更改爲'public const int x = 6'有什麼不同? – Evorlor

+0

@Evorlor效果沒有什麼不同。但在你的情況下,相對於將計算結果分配給常量而言,發生更改時(相對)非常明顯。 –