如何将枚举数作为常量?

时间:2016-04-05 17:05:17

标签: c# unity3d enums const constants

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;

以上不起作用,因为枚举计数不是常数,所以我不能将它分配给常量变量。

如何将枚举数作为常量?

6 个答案:

答案 0 :(得分:10)

无法将枚举数量设为constEnum.GetNames使用反射来获取这些东西,而这本身就是一个运行时操作。因此,在编译时无法知道,这是const的必要条件。

答案 1 :(得分:3)

不幸的是,由于技术限制,这不可能以任何有意义的方式进行:

  • [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团队采取了一些权衡措施来改善您在其他地方的体验。

答案 2 :(得分:2)

假设您需要const,因为您正在尝试为属性(this one?)指定值,那么您就不走运了。

您的选择是:

  1. 对属性声明中的计数进行硬编码,或将其作为const本身进行硬编码,并注意使计数与enum定义保持同步
  2. 使用PostSharp或其他面向方面的框架为您插入属性。从来没有这样做过,但看起来可能(How to inject an attribute using a PostSharp attribute?
  3. 你可能也可以通过某种方式使用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();
        }
    }
    

答案 3 :(得分:1)

您无法在运行时分配/创建const。这就是你想要做的。 const必须是fully evaluated at compile time,而不是运行时。

我不确定Unity (以及为什么需要const,但我会寻找使用readonly

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

答案 4 :(得分:1)

这里玩得太晚了,但在使用枚举默认值(即没有自定义值)时,我总是使用一个漂亮的技巧。

public enum MyEnum {
    foo,
    bar,
    baz,
    count
}

由于枚举默认从 0 开始,确保最后一个条目被简单地命名为 'count' 确保 count 的值实际上是枚举本身中值的数量。

private const int constEnumCount = (int)MyEnum.count; 

干杯!

答案 5 :(得分:0)

在C#中,const被定义为常量,即值(不是产生它的计算!)直接嵌入到编译的程序集中。

为了阻止您做一些有问题的事情,编译器不允许您将计算结果分配给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才能获得正确的值。

情况变得更糟:想象一下你有一个使用B.NumberOfFoos的程序集C.dll。值2将直接嵌入到C.dll中,因此 需要重新编译, B.dll之后,才能获得正确的值。因此(成功的坑),C#不允许你对const进行分配。