【问题标题】:How can I get the number of enums as a constant?如何将枚举数作为常数?
【发布时间】:2016-07-25 18:30:18
【问题描述】:

来自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;

上面的不行,因为枚举计数不是常数,所以不能赋值给常数变量。

如何将枚举数作为常数?

【问题讨论】:

  • 在将其定义为const 时应该小心,因为在编译时需要该值,并且编译时的值将在所有引用的程序集中传播。是否可以改为使用只读?
  • @JoeBlow 这是开发者可以在游戏测试中摆动一个数字的范围。它用于编辑引擎,以便在运行之前和运行期间对代码进行更改。
  • 啊,你的意思是 EDITOR INSPECTOR 属性!很像 [Header("Hi there")]。不,你绝对不能以任何方式改变它。
  • 大声笑老兄,您尝试做的事情的解决方案非常简单。我会回答的

标签: c# unity3d enums constants


【解决方案1】:

不可能将枚举数作为const 获取。 Enum.GetNames 使用反射来获取这些东西,这本质上是一个运行时操作。因此在编译时无法知道,这是const的要求。

【讨论】:

    【解决方案2】:

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

    • [Range(int,int)] 是一个属性,所有提供给属性的信息都必须是一个常量
    • 获取枚举中值数量的唯一真正安全的方法是使用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 团队采取了一些权衡措施来改善您在其他地方的体验。

    【讨论】:

    • 这与 C# 编译器不够聪明无关。 const 定义为常量。计算值不是:如果枚举(例如在不同的程序集中定义)突然具有更多或更少的值,则计算值将需要更改。这就是为什么需要在运行时计算该值。
    • @DanielRose,与 C 或 Java 相比,C# 无法评估(在编写答案时)const int myval = 3 + 5 + 8;,即使答案是 常量。这就是我所指的。一些语言会在初始化时评估常量。您可以使用static readonly 字段,但不能使用类/方法/参数属性。
    【解决方案3】:

    假设您需要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();
        }
    }
    

    【讨论】:

      【解决方案4】:

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

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

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

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

      干杯!

      【讨论】:

      • 这可能会有所帮助,但会导致大量代码“忽略”最后一项。刚才我做了类似的事情,但只是使用了最后一个实际的 enum + 1;
      【解决方案5】:

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

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

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

      【讨论】:

      • 不幸的是,Unity 不接受只读的。我想我应该问为什么不能在编译时计算枚举的长度。
      • 作者尝试将此值用作属性参数,这就是为什么它需要 const
      【解决方案6】:

      在 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 进行赋值。

      【讨论】:

      • 这与将public const int x = 5; 更改为public const int x = 6 有何不同?
      • @Evorlor 效果不一样。但是在您的情况下,与将计算结果分配给 const 相比,发生更改时(相对)明显。
      猜你喜欢
      • 2017-05-29
      • 1970-01-01
      • 2013-12-13
      • 2011-12-04
      • 1970-01-01
      • 2012-04-18
      • 2011-05-30
      • 2021-03-10
      • 2013-01-17
      相关资源
      最近更新 更多