28

To constrain a generic type parameter to be of an enum type, I previously constrained them like this, which was the best I could go for constraining type T for enums in pre-C# 7.3:

void DoSomething<T>() where T : struct, IComparable, IConvertible, IFormattable

Now, C# 7.3 adds a new feature to constrain a generic type to System.Enum. I tried using the enum constraint with the VS2017 15.7 update released today, and it compiles successfully when I write it like this (given I have a using System; directive):

void DoSomething<T>() where T : Enum

However, using the enum keyword does not work and causes the compiler to throw the following errors (there are more errors following, expecting a method body, but not really worth mentioning here I guess):

void DoSomething<T>() where T : enum
                                ^ error CS1031: Type expected
                                  error CS1002: ; expected
                                    ^ error CS1001: Identifier expected
                                      error CS1514: { expected
                                      error CS1513: } expected

Since there is a struct constraint working for structures, I do not understand why enum doesn't work here for enums. It's true that enum does not map to an actual type like int would do for Int32, but I thought it should behave the same as the struct constraint.

Did I just fall into an experimental feature trap not being fully implemented yet, or was this done on purpose in the specification (why?)?

0xced
  • 25,219
  • 10
  • 103
  • 255
Ray
  • 7,940
  • 7
  • 58
  • 90
  • While `string` is an alias for the `String` type, the same is not true for the `enum` keyword and the `Enum` type. –  May 07 '18 at 16:42
  • That's what I meant with my last paragraph. A `struct` constraint is not an alias for a specific type too, but is possible. – Ray May 07 '18 at 16:43
  • `struct` is for the value types, not just struct, similar to `class` for all reference types – Mrinal Kamboj May 07 '18 at 16:51
  • check this, possible solution https://stackoverflow.com/a/79903/1559611 – Mrinal Kamboj May 07 '18 at 16:56
  • @MrinalKamboj It does make sense, kinda. Though I think one could argue then that those keywords handle their own specific cases, and `enum` should've done too. About your link: with C# 7.3 using `Enum` (not the keyword `enum`) would be the better solution now. The question is just why the keyword isn't meant to work (at the moment). – Ray May 07 '18 at 16:58
  • This implementation caught me out. My question [Why is a generic type constrained by 'Enum' failing to qualify as a 'struct'?](https://stackoverflow.com/q/50294032/397817) may or may not be of interest :) – Stephen Kennedy May 11 '18 at 14:06

1 Answers1

18

The struct constraint on generics doesn't map to an actual type (though it could, in theory, map to ValueType). Similarly, enum doesn't cleanly map to actual types the way string, int, or long do, it sets up special syntax for creating a class of symbolic constants that map to integer values; hence public enum Stuff instead of public class Stuff : Enum. Note that had the latter been implemented instead, it would be more subtle since it would change syntax based on inherited type, instead of changing syntax based on a non-class keyword.

So, in conclusion, yes, where T : enum is not meant to work because enum is a keyword, not a type alias. If you really want to see it work because enum at least smells like a type alias in context like these, go request it!

EDIT: For some historical reference, here's a question from 2008 indicating that Enum was not a valid constraint, since it's a special class.

David
  • 10,458
  • 1
  • 28
  • 40
  • I guess this is the best explanation so far. I couldn't find others on GitHub where the implementation of this feature was discussed. Funny enough, people there originally provided samples which also used the `enum` keyword, but it wasn't implemented like that in the end. – Ray May 08 '18 at 20:45
  • 3
    @RayKoopa Also, I am a bit surprised that if you have just `where T : Enum`, then the compiler will allow you to use the abstract base type `System.Enum` itself (a reference type!) for `T`. So you could `DoSomething();`. To avoid that, it is actually legal to do `where T : struct, Enum`. To me it looks like that is what you want 99.9% of the time! Ah, I see now there is a linked thread about just this. – Jeppe Stig Nielsen May 29 '18 at 10:15