29

Possible Duplicates:
Anyone know a good workaround for the lack of an enum generic constraint?
Create Generic method constraining T to an Enum

Is is possible to limit the generic type parameter [I don't know if that's the right name] to an Enum?

For example how do I do something like this?

//VB.NET
Function GetValues(Of T As System.Enum)(ByVal value As T) As IEnumerable(Of T)
    Return [Enum].GetValues(value.GetType)
End Function

//C#
public IEnumerable<T> GetValues<T>(T value) where T : System.Enum
{
    return Enum.GetValues(value.GetType());
}

Update

I eventually used Jon Skeet's Unconstrained Melody for that purpose. Thanks to you all for your contributions.

Community
  • 1
  • 1
Alex Essilfie
  • 12,339
  • 9
  • 70
  • 108
  • Why not just use the enum instead of the generic?? – Tony Abrams Feb 21 '11 at 15:28
  • Another possible duplicate: [Create Generic method constraining T to an Enum](http://stackoverflow.com/questions/79126/create-generic-method-constraining-t-to-an-enum) – Metro Smurf Feb 21 '11 at 16:26
  • Vote for it here http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2557231-enums-constraint-for-generics then wait a decade for the C# team to get around to it. – Martin Brown Dec 22 '15 at 16:58
  • +Martin Brown, you're confusing C# with Java :) – nurchi Nov 21 '16 at 21:38

4 Answers4

17

You can't. An alternative solution is using struct and run-time check.

public IEnumerable<T> GetValues<T>(T value) where T : struct
{  
    if (!typeof(T).IsEnum) throw new NotSupportedException();
    return (IEnumerable<T>)Enum.GetValues(value.GetType()); 
} 
Cheng Chen
  • 42,509
  • 16
  • 113
  • 174
  • This won't return the desired result, if it even compiles. `Enum.GetValues` returns an `Array` (whose elements are just Objects), which does not cast implicitly to an `IEnumerable`. – KeithS Feb 21 '11 at 16:05
  • Unfortunately, your solution will not return a generic enumerable list. It provided me with some insight though. Thanks. – Alex Essilfie Feb 22 '11 at 09:05
  • As an aside, I implemented your solution and [@Keiths'](http://stackoverflow.com/questions/5067724/how-to-limit-a-generic-type-parameter-to-system-enum/5068301#5068301) for a different, though related, purpose. – Alex Essilfie Feb 22 '11 at 09:08
8

Unfortunately, you cannot - Microsoft closed this one out as a won't fix item.

You can treat enums as structs and use that as the constraint instead (I think that was how Jon Skeet did it in Unconstrained Melody?) but that is kind of unsightly.

48klocs
  • 6,073
  • 3
  • 27
  • 34
  • 2
    Thanks for pointing out Jon Skeet's Unconstrained Melody. It was very helpful. – Alex Essilfie Feb 22 '11 at 09:03
  • C# 7.3 actually added the ability to constrain generic restraints by enums directly - [source](https://blogs.msdn.microsoft.com/dotnet/2018/04/18/visual-studio-2017-15-7-preview-4/) – Tyler Gill May 07 '18 at 21:38
6

Matt's and Danny's answers both have half the answer. This should actually get you what you need:

public IEnumerable<T> GetValues<T>() where T : struct
{   
    if (!typeof(T).IsEnum) throw new InvalidOperationException("Generic type argument is not a System.Enum");
    return Enum.GetValues(typeof(T)).OfType<T>(); 
} 

Changes from Danny's answer:

  • Though having a parameter of the generic type allows for type inference, since the value is not actually used it is more proper to explicitly specify the generic type (like with the Linq methods that don't take parameters).
  • Enum.GetValues() returns an Array of Objects, which will not cast implicitly to an IEnumerable of T. The extra Linq method to cast the results (technically OfType is a filter operation but in this case it'll return everything) is necessary to conform to the return type.
  • Optional: though NotSupportedException is as good a choice as any for an exception to throw, there are other options; ArgumentException, InvalidOperationException, InvalidCastException, etc. I chose InvalidOperationException because that's what it is; an invalid attempt to get enum values from a non-enum type. This is semantic and I'm not going to argue with anyone else's logic.
KeithS
  • 70,210
  • 21
  • 112
  • 164
  • Why not just use the Cast method? `return Enum.GetValues(typeof(T)).Cast()` – Metro Smurf Feb 21 '11 at 16:28
  • it would probably work in this case, however there are several questions on this site dealing with cases where it doesn't work the way users expect. I prefer to avoid it in favor of OfType or Select, which will do the job in an unambiguous way. – KeithS Feb 21 '11 at 16:41
  • As an aside, I implemented your solution and [@Danny Chen's](http://stackoverflow.com/questions/5067724/how-to-limit-a-generic-type-parameter-to-system-enum/5067786#5067786) for a different, though related, purpose. – Alex Essilfie Feb 22 '11 at 09:10
3

There is no need to make your method generic in this way.

You can just use the System.Enum as the type parameter in your return type:

using System.Linq;
.
.
.
public IEnumerable<Enum> GetValues(Enum value)
{
    return Enum.GetValues(value.GetType()).OfType<Enum>();
}
Matt Ellen
  • 11,268
  • 4
  • 68
  • 90
  • 1
    Unfortunately, this isn't strongly typed to the Enum that was passed in; you get a list containing System.Enums that you then have to cast to the proper type. – KeithS Feb 21 '11 at 15:56
  • @KeithS: as you would with Danny Chen's answer, and the OP's original code (that wouldn't even work, in fact, as `Array` isn't generic). – Matt Ellen Feb 21 '11 at 16:00
  • No. The generic returns a strongly-typed list of values of that particular enum type; no casting of the return type is necessary (unless you want the numbers, or strings, etc). However, his doesn't work for other reasons. – KeithS Feb 21 '11 at 16:03