【问题标题】:Why can't I get the count of enums during compile time?为什么我不能在编译期间获得枚举计数?
【发布时间】:2016-04-06 14:08:13
【问题描述】:

我问过How can I get the number of enums as a constant?,我发现在编译期间我无法获取枚举的计数,因为 C# 使用反射来做到这一点。

看了What is reflection and why is it useful?,所以对反射有了非常基本的了解。

要获取枚举数,我可以使用Enum.GetNames(typeof(Item.Type)).Length;,这会在运行时使用反射发生。

我没有看到获取枚举计数所需的任何运行时知识,因为据我所知,在运行时无法更改枚举计数。

为什么 C# 必须使用反射来获取枚举数?为什么编译时不能这样做?

【问题讨论】:

  • 有趣,似乎是因为反射只在运行时起作用。
  • @Evorlor 好吧,如果你需要计算枚举,你可以使用集合。枚举用于定义元素而不是用于使用它们。
  • @ehh 他在这个问题中发布了这个问题,并且是一个与这个问题不同的问题。
  • 我认为它与普通课程非常相似。如果不使用反射,就无法获得类的属性数量(至少这是我知道的唯一方法),而且我们知道,反射直到运行时才会发生。 为什么你必须使用反射,你可能不得不问.NET的开发人员。
  • “为什么缺少 C# 功能” - 我认为 Eric Lippert 对此有标准答案。

标签: c# reflection enums count compile-time


【解决方案1】:

仅仅因为某些东西可以在编译时被评估并不意味着有人已经对编译器进行了编程来这样做。 string.Format("{0:N2}", Math.PI) 是另一个例子。

目前获取Enum 的值数量的唯一方法是使用反射(Enum.GetNames 或类似的东西)。所以它不是一个常量表达式,尽管技术上编译器可以只是在编译时评估表达式并确定结果是什么。

nameof 就是一个很好的例子。它在编译时是恒定的,但在有人设计、开发、测试、记录和发布该功能之前,没有机制可以在编译时提取结果。这些都不是免费的,因此该想法必须与其他可能更有价值的功能竞争宝贵的资源(人员、时间、金钱)。

因此,如果您认为像 enumcount(Item.Type) 这样的编译时构造是该语言的一个有价值的补充,那么非常欢迎您在 Connect 上发表建议,看看它是否能排在首位功能列表。

但是,我需要这个数字作为一个常数,这样我就可以在 Unity 的 [Range(int, int)] 功能中使用它。

一个不理想的解决方法是定义一个与当前枚举项数匹配的常量,如果计数不匹配,则在运行时抛出异常:

在您的枚举旁边定义一个公共常量,对其进行注释以便开发人员知道要更新它:

// Update this value any time the Type enum is updated
public const int TypeCount = 5;
public Enum Type
{
    Bar1,
    Bar2,
    Bar3,
    Bar4,
    Bar5,
}

在你的属性中使用它:

[Range(0, Item.TypeCount)]
public void BlahBlahBlah() {}

并在您的应用开始时检查它:

public static Main()
{
   if(Enum.GetNames(typeof(Item.Type)).Length != Item.TypeCount)
      throw new ApplicationException ("TypeCount and number of Types do not match.\nPlease update TypeCount constant.")
}

【讨论】:

  • 我有一个奇怪的想法(我根本没有测试过)是从 Unity 的RangeAttribute 继承并创建一个可以评估枚举类型计数的自定义的可能性。我不确定这是否可行,因为我不确定 Unity 是否会正确选择子类属性来代替基类。也可能是RangeAttribute 被密封,而整个问题仍然没有实际意义。
  • 您的答案的后半部分已经发布在我的链接问题之一中,在我看来并不适合这个问题。但是谢谢你的回答。我不知道它只是没有被开发出来。我认为存在某种技术限制。
  • @Kyle 属性是元数据,需要编译时常量。这并不是说无论消耗属性在运行时无法获取数据,而是属性本身中的数据需要保持不变。
  • @Evorlor 公平地说,可能存在我不知道的技术限制或边缘情况 - 但从表面上看,这确实可能。价值是否能证明成本是另一个问题。
  • @DStanley 只有属性构造函数参数需要是编译时常量。您可以在属性构造函数中进行运行时计算,因为它只有在反射 API 获取属性实例后才会执行。因此,该属性可以将Type 作为其参数(表示枚举类型)并在其构造函数中执行Enum.GetNames( type ).Length(并将其分配给属性)。我至少已经测试了这么多,而且效果很好。
【解决方案2】:

我认为简单来说:

枚举是一种“类型定义”,.NET 在需要“类型描述符导航”时使用反射。

所以 Enums 是一个 Type 和 @runtime,如果你想计算定义的 enums 声音你需要用户一个反射。

【讨论】:

    【解决方案3】:

    我没有看到获取枚举计数所需的任何运行时知识, 因为据我所知,枚举的数量不能在 运行时。

    这是您推理中的错误:是的,枚举的计数不能在期间运行时更改。但是,它可以在 之间 运行时更改:

    A.dll - version 1
      public enum Foo { A }
    
    A.dll - version 2
      public enum Foo { Bar, Baz }
    

    将版本 1 的 A.dll 替换为版本 2。枚举的数量已更改(以及值的名称)。

    为什么 C# 必须使用反射来获取枚举数?为什么 编译时不能这样做吗?

    它可以这样做。但是你会遇到上面的问题。编译时计算的值可能不正确。

    【讨论】:

      猜你喜欢
      • 2020-01-04
      • 1970-01-01
      • 2022-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多