9

I have asked How can I get the number of enums as a constant?, and I found out that I cannot get the count of enums during compile time, because C# uses reflection to do so.

I read What is reflection and why is it useful?, so I have a very basic understanding of reflection.

To get the count of enums, I can use Enum.GetNames(typeof(Item.Type)).Length;, and this happens during runtime using reflection.

I don't see any runtime knowledge needed to get the count of enums, because as far as I know, the count of enums cannot be changed during runtime.

Why does C# have to use reflection to get the count of enums? Why can't it do so during compile time?

7
  • Interesting, seem to be because reflection is working only at runtime.
    – ehh
    Commented Apr 6, 2016 at 14:12
  • @Evorlor Well, if you need to count enums you can use collections intead. Enums are for defining elements not for working with them.
    – Fabjan
    Commented Apr 6, 2016 at 14:23
  • 1
    @ehh He posted that in this question, and is a separate question from this one. Commented Apr 6, 2016 at 14:23
  • 1
    I assume it's very similar to a normal class. You can't get the number of properties of a class without using reflection (at least that's the only way I know how to do it) and as we know, reflection doesn't happen until runtime. Why you have to use reflection, you'd probably have to ask the devs at .NET. Commented Apr 6, 2016 at 14:26
  • 3
    "Why is C# feature missing" - I think Eric Lippert has standard answer to that.
    – Ivan Stoev
    Commented Apr 6, 2016 at 14:27

3 Answers 3

6

Just because something can be evaluated at compile time doesn't mean that someone has programmed the compiler to do so. string.Format("{0:N2}", Math.PI) is another example.

The only way at present to get the count of the number of values of an Enum is by using reflection (Enum.GetNames or something similar). So it is not a constant expression, although technically the compiler could just evaluate the expression at compile-time and determine what the result is.

nameof is a perfect example. It is constant at compile-time, but there was no mechanism to extract the result at compile time until someone designed, developed, tested, documented, and shipped the feature. None of those are free, and thus the idea must compete for valuable resources (people, time, money) against other features that may be more valuable.

So if you feel that a compile-time construct like enumcount(Item.Type) is a valuable addition to the language, then you are more than welcome to post a suggestion on Connect and see if it makes it to the top of the feature list.

But, I need this number as a constant number, so that I can use it in Unity's [Range(int, int)] feature.

One non-ideal workaround is to define a constant that matches the current number of enum items, and throw an exception at run-time if the counts do not match:

Define a public constant right next to your enum, commenting it so that developers know to update it:

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

use it in your attribute:

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

and check it at the start of your app:

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.")
}
5
  • One curious thought I had (which I haven't tested at all) is the possibility of inheriting from Unity's RangeAttribute and creating a custom one that would evaluate the enum type count. I'm not sure this would work because I'm not sure if Unity would correctly pick up the subclassed attribute in place of the base class. It could also be the case that RangeAttribute is sealed and the whole point is moot anyway.
    – Kyle
    Commented Apr 6, 2016 at 15:13
  • The second half of your answer is already posted in one of my linked questions, and not really suited for this question in my opinion. But thank you for your answer. I did not know that it just hadn't been developed. I thought there was some sort of technical limitation.
    – Evorlor
    Commented Apr 6, 2016 at 15:19
  • 1
    @Kyle Attributes are meta-data are require compile-time constants. That's not to say that whatever consumes the attribute couldn't get data at run time but the data in the attribute itself needs to be constant.
    – D Stanley
    Commented Apr 6, 2016 at 16:11
  • 1
    @Evorlor Just to be fair, there may be technical limitations or edge cases that I'm not aware of - but at face value it certainly seems possible. Whether the value justifies the cost is a different question.
    – D Stanley
    Commented Apr 6, 2016 at 16:13
  • 1
    @DStanley Only the attribute constructor parameters need to be compile-time constants. You can do run-time computations in the attribute constructor since it only gets executed once the reflection API gets the attribute instance. So the attribute could take a Type as its parameter (representing the enum type) and do the Enum.GetNames( type ).Length in its constructor (and assign it to a property). I've tested at least that much and it works.
    – Kyle
    Commented Apr 6, 2016 at 17:48
0

I think in simple terms:

Enums is one "type definition", .NET use Reflection when "type descriptor navigation" is needed.

So Enums is a Type and @ runtime, if you want to count the defined enums voice you need to user a reflection.

0

I don't see any runtime knowledge needed to get the count of enums, because as far as I know, the count of enums cannot be changed during runtime.

Here is the mistake in your reasoning: Yes, the count of enums cannot be changed during runtime. However, it can be changed between runtime:

A.dll - version 1
  public enum Foo { A }

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

Replace version 1 of A.dll with version 2. The count of enums has changed (and the names of the values as well).

Why does C# have to use reflection to get the count of enums? Why can't it do so during compile time?

It could do so. But then you would run into the problem above. The compile-time calculated value could become incorrect.

Not the answer you're looking for? Browse other questions tagged or ask your own question.