2016-04-05 199 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效果没有什么不同。但在你的情况下,相对于将计算结果分配给常量而言,发生更改时(相对)非常明显。 –