14

I've been trying to create an extension method, that would work on any enum, to return its values.

Instead of doing this:

Enum.GetValues(typeof(BiasCode)).Cast<BiasCode>()

It would be nice to do this:

new BiasCode().Values()

It would even be better without new, but that's another issue.

I have a .NET fiddle that has a solution that's close (code shown below). The problem with this code is that the extension method is returning List<int>. I would like to have it return a list of the enum values itself. Returning List<int> isn't terrible; it just means I have to cast the result.

Is it even possible to do this? I tried making the extension method generic, but ran into problems. This is as close as I was able to get:

using System;
using System.Linq;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        foreach (int biasCode in new BiasCode().Values())
        {
            DisplayEnum((BiasCode)biasCode);
        }
    }

    public static void DisplayEnum(BiasCode biasCode)
    {
        Console.WriteLine(biasCode);    
    }
}

public enum BiasCode
{
    Unknown,
    OC,
    MPP
}

public static class EnumExtensions
{
    public static List<int> Values(this Enum theEnum)
    {
        var enumValues = new List<int>();
        foreach (int enumValue in Enum.GetValues(theEnum.GetType()))
        {
            enumValues.Add(enumValue);
        }

        return enumValues;
    }
}
neontapir
  • 4,698
  • 3
  • 37
  • 52
Bob Horn
  • 33,387
  • 34
  • 113
  • 219
  • After having a separate discussion with Jon Skeet about this issue, I've accepted the answer that isn't even an extension method. Seems like the right thing to do. Discussion with Jon: http://stackoverflow.com/a/23808495/279516 – Bob Horn May 22 '14 at 14:22

6 Answers6

8

You can return an instance of the appropriate enum type (created using reflection), but its static type cannot be List<EnumType>. That would require EnumType to be a generic type parameter of the method, but then the type would have to be constrained to only enum types and that is not possible in C#.

However, you can get close enough in practice (and add runtime checks to top it off) so you can write a method that works like this:

public static IEnumerable<TEnum> Values<TEnum>()
where TEnum : struct,  IComparable, IFormattable, IConvertible
{
    var enumType = typeof(TEnum);

    // Optional runtime check for completeness    
    if(!enumType.IsEnum)
    {
        throw new ArgumentException();
    }

    return Enum.GetValues(enumType).Cast<TEnum>();
}

which you can invoke with

var values = Values<BiasCode>();

I have made the method return IEnumerable<TEnum> instead of a list for the extra LINQ-y flavor, but you can trivially return a real list with .ToList() on the return value.

Community
  • 1
  • 1
Jon
  • 428,835
  • 81
  • 738
  • 806
  • Are IComparable, IFormattable, and IConvertible used as the type constraints because those are the interfaces that enum implements? – Bob Horn May 21 '14 at 23:12
  • @BobHorn: Yes. They are for getting "as close as possible" to a proper `enum` constraint. – Jon May 21 '14 at 23:19
  • I just added a new answer to this post. Could you tell me if there is some reason that it's not a good way to solve this? – Bob Horn May 22 '14 at 03:16
  • @BobHorn: a) You don't get adequately strong typing -- what are you going to do with those values other than print them? Given a `List` and no other information, it's pretty hard to do anything meaningful with it, and b) The required syntax `new Foo.Values()` or `Foo.SomeValue.Values()` is unfortunate at best. – Jon May 22 '14 at 08:07
  • I could use some help in understanding what you mean. I'm not sure what I've lost. As I just replied to Edin, the fiddle shows that the caller doesn't have to cast again. – Bob Horn May 22 '14 at 12:30
  • @BobHorn: `foreach(T value in container)` does an implicit cast to `T` (section 8.8.4 of the spec), so you *are* casting but the cast is effectively hidden inside "other code". – Jon May 22 '14 at 12:46
  • Ahhhhh. Maybe I'm okay with that. ;) I'll need to put some more thought into it. Thanks! – Bob Horn May 22 '14 at 12:59
7

You could declare your method like this:

public static List<T> Values<T>() where T : struct
{
    var type = typeof(T);

    if(!type.IsEnum) return null; // or throw exception

    return Enum.GetValues(type).Cast<T>().ToList();
}

Then you can call it

Values<BiasCode>();
Selman Genç
  • 100,147
  • 13
  • 119
  • 184
  • 1
    Since this isn't an extension method, would this just go in a static utility class somewhere? Then the call would be something like: `EnumUtility.Values();` – Bob Horn May 21 '14 at 23:02
  • @BobHorn yes if you put this method into a class or static an extension class you should specify the name of the class as well. – Selman Genç May 21 '14 at 23:04
  • I just added a new answer to this post. Could you tell me if there is some reason that it's not a good way to solve this? – Bob Horn May 22 '14 at 03:16
5

I'm wondering if I'm missing something because all of the answers use a generic method as part of the solution. Why not just do something like this?

public static List<Enum> Values(this Enum theEnum)
{
    return Enum.GetValues(theEnum.GetType()).Cast<Enum>().ToList();
}

The fiddle is here: https://dotnetfiddle.net/FRDuvD

This way this extension method will only be available to enums. Using the generics approach, the extension method seems to be available to all types:

string someString = "x";
someString.Values();

It would be better not to have Values() available to a string at compile time.

Bob Horn
  • 33,387
  • 34
  • 113
  • 219
  • I think we could all agree that there is no the best solution for this, since it is not possible to do a where clause constraint on Enum type. What you do better with this solution is the constraint only on Enum values. However, in comparison to my solution, you lose the information about the enum type, on which you are invoking your extension method. So you need to cast it again. So, I do not see much difference between this and the approach originally posted in your question, where you return List and need to cast it. – Edin May 22 '14 at 07:40
  • About this: "...you lose the information about the enum type..." And this: "So you need to cast it again." Did you see the fiddle? The caller didn't need to cast again. The caller could pass the enum to the DisplayEnum() method without a cast. Also, the DisplayEnum() method could use a specific enum as its parameter, instead of just accepting Enum. – Bob Horn May 22 '14 at 12:27
1

Since you are looking for the extension method, here it is:

public static class EnumExtensions
{
   public static List<T> Values<T>(this T theEnum)
       where T : struct,  IComparable, IFormattable, IConvertible
   {
       if (!typeof(T).IsEnum) 
           throw new InvalidOperationException(string.Format("Type {0} is not enum.", typeof(T).FullName)); 

       return Enum.GetValues(theEnum.GetType()).Cast<T>().ToList();
   }
}

EDIT

I will incorporate my comment in Bob's answer here:

So, I think we could all agree that there is no best solution for this, since it is not possible to do a where clause constraint on Enum type. Another solution would be to have a following method signature (as Bob suggested):

public static List<Enum> Values(this Enum theEnum)

What we would do better with this solution is the constraint only on Enum values. However, in comparison to the generic solution, we lose the information about the enum type, on which we are invoking your extension method. So we need to cast it again. And I do not see much difference between this and the approach originally posted in by Bob in his question, where he returns List<int> and needs to cast it back to our enum.

The most elegant solution can be achieved by using Code Contracts with static checking. However it requires usage of the code contracts and it would probably an overkill if they are to be used in this single case. This approach was addressed in the following thread: Using code contracts to make a generic to be of type enum

Community
  • 1
  • 1
Edin
  • 1,476
  • 11
  • 21
  • How would you call this? Would you have to create an instance of the enum by just using one of its elements? – Bob Horn May 21 '14 at 22:44
  • Yes. Since it is an extension method, you need an instance: List values = SomeEnumType.A.Values(); – Edin May 21 '14 at 22:55
  • I created a fiddle for this: https://dotnetfiddle.net/E3wb1w. Looks like instead of having to pick an enum value, I can also do this: `new BiasCode().Values()`. I like that because it's less confusing to me. – Bob Horn May 21 '14 at 23:22
  • Can you explain how the extension method is recognized on an enum? If the call is this: `new BiasCode().Values()`, since the extension method is generic, how does the compiler know that this extension method is applicable to my enum? (Especially since no generic type parameter is specified in the calling code.) – Bob Horn May 21 '14 at 23:24
  • Here is my guess: Since the extension method is for a generic type, whenever Values() is called, the type that is used is on whatever object is calling Values(). That would mean that this extension method is available to all objects, unless it's also constrained at compile time by the constraints specified in the signature of the extension method. – Bob Horn May 21 '14 at 23:34
  • I'm liking this approach. It's not cool that this compiles: `int someInt = 4; someInt.Values();`. (It's a run-time error.) But no one should be writing that code anyway. – Bob Horn May 21 '14 at 23:37
  • I just added a new answer to this post. Could you tell me if there is some reason that it's not a good way to solve this? – Bob Horn May 22 '14 at 03:15
  • I unaccepted your answer for now. If there is something wrong with my new answer, I'll accept yours again. – Bob Horn May 22 '14 at 03:28
1

How about this

class Program{
    static void Main(string[] args)
    {
        BiasCode b = BiasCode.MPP;
        var these = b.Values().ToList();
        //...  these contains the actual values as the enum type
    }
}
public static class EnumExtensions
{
    public static IEnumerable<T> Values<T>(this T theEnum) where T : struct,IComparable, IFormattable, IConvertible
    {
        var enumValues = new List<T>();

        if ( !(theEnum is Enum))
            throw new ArgumentException("must me an enum");

        return Enum.GetValues(typeof(T)).Cast<T>();
    }
}
cechode
  • 1,002
  • 1
  • 8
  • 20
  • I just added a new answer to this post. Could you tell me if there is some reason that it's not a good way to solve this? – Bob Horn May 22 '14 at 03:18
1

Extension method to get the values of any enum in C#

Based on solutions above with slightly different approach:

public static class Enum<TEnum> where TEnum : struct, IComparable, IFormattable, IConvertible
{
    public static IEnumerable<TEnum> GetAll()
    {
        var t = typeof(TEnum);
        if (!t.IsEnum)
            throw new ArgumentException();

        return Enum.GetValues(t).Cast<TEnum>();
    }
}

Usage

var values = Enum<MyEnum>.GetAll();

Test

public enum Test
{
    First,
    Second,
    Third
}

[TestClass]
public class EnumTests
{
    [TestMethod]
    public void MyTestMethod()
    {
        var values = Enum<Test>.GetAll();
        Assert.AreEqual(3, values.Count());
    }
}

Jan Setzer
  • 143
  • 8