47

I have an enumeration for my Things like so:

public enum Things
{
   OneThing,
   AnotherThing
}

I would like to write an extension method for this enumeration (similar to Prise's answer here) but while that method works on an instance of the enumeration, ala

Things thing; var list = thing.ToSelectList();

I would like it to work on the actual enumeration instead:

var list = Things.ToSelectList();

I could just do

var list = default(Things).ToSelectList();

But I don't like the look of that :)

I have gotten closer with the following extension method:

public static SelectList ToSelectList(this Type type)
{
   if (type.IsEnum)
   {
      var values = from Enum e in Enum.GetValues(type)
                   select new { ID = e, Name = e.ToString() };
      return new SelectList(values, "Id", "Name");
   }
   else
   {
      return null;
   }
}

Used like so:

var list = typeof(Things).ToSelectList();

Can we do any better than that?

Community
  • 1
  • 1
Jamezor
  • 2,488
  • 3
  • 23
  • 16
  • You can make a class that masquerades as an enum. The downsides are mostly non-standard programming, I guess, and the inability to get rid of the methods inherited from Object. – Iucounu May 31 '13 at 16:43
  • I don't think it is a right practice to use verb for names of classes. ideally class name should be noun and meaningful in itself. – user3264784 Feb 27 '14 at 11:36

7 Answers7

76

Extension methods only work on instances, so it can't be done, but with some well-chosen class/method names and generics, you can produce a result that looks just as good:

public class SelectList
{
    // Normal SelectList properties/methods go here

    public static SelectList Of<T>()
    {
        Type t = typeof(T);
        if (t.IsEnum)
        {
            var values = from Enum e in Enum.GetValues(type)
                         select new { ID = e, Name = e.ToString() };
            return new SelectList(values, "Id", "Name");
        }
        return null;
    }
}

Then you can get your select list like this:

var list = SelectList.Of<Things>();

IMO this reads a lot better than Things.ToSelectList().

Aaronaught
  • 120,909
  • 25
  • 266
  • 342
  • 3
    this is so brilliant i actually laughed out loud when i read it. i was so focused on making an enum to a selectlist i never thought of doing it the other way around. – spaceman Sep 21 '11 at 19:41
  • You can make a class that masquerades as an enum. The downsides are mostly non-standard programming, I guess, and the inability to get rid of the methods inherited from Object. – Iucounu May 31 '13 at 16:42
  • 3
    @Iucounu: The downside is... it's not an enum. You don't get range checking, numeric representation, simple database storage, automatic string conversion, or any of the other benefits of an enum. Given those downsides and no real upside, I can't think of why you'd want to do it. – Aaronaught Jun 01 '13 at 03:14
  • It provides the programming semantics of an enum with a static method, which making a good-looking method just doesn't do. I don't think you understood the code sample below; you certainly can have numeric representation and string conversion, nor is database storage a downside. It's at least a solution... – Iucounu Jun 01 '13 at 03:18
  • 4
    @Iucounu: I understood it perfectly well, it's the "typesafe enum" pattern from stone-age Java before the language designers finally wised up and implemented actual enums. Your solution *isn't* a solution because the question was about actual enums, and besides which, the comments to other answers are hardly the appropriate medium in which to be advertising your own. – Aaronaught Jun 01 '13 at 03:22
  • New to C#, getting a compiler error at `public static Of()`: _"The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?)"_. Can you assist? – InteXX Jul 18 '14 at 22:26
  • @InteXX: Forgot a keyword. Updated. – Aaronaught Jul 19 '14 at 04:37
  • Got it, thanks. This looks good, really looking forward to using it. Now I'm getting _"'SelectList' does not contain a constructor that takes 3 arguments"_. Do you have a favorite constructor that you like to use with it? – InteXX Jul 19 '14 at 07:25
  • @InteXX: This isn't a complete program, it's a code snippet. You can either write the rest of the constructors/methods yourself or reference the [MVC class](http://msdn.microsoft.com/en-us/library/system.web.mvc.selectlist(v=vs.118).aspx) by namespace. If you're new to C# then I suggest attempting some more basic programs before applying concepts like this. – Aaronaught Jul 19 '14 at 14:08
  • The variable "type" GetValues(type) is not defined. If this code is partial or specific to MVC then please make it clear or even better, fix it so we don't have to learn anything else but C# to understand it – PandaWood Dec 13 '16 at 02:20
5

Aaronaught's answer is really great, based on that I made the following implementation:

public class SelectList
{
    public static IEnumerable<Enum> Of<T>() where T : struct, IConvertible
    {
        Type t = typeof(T);
        if (t.IsEnum)
        {
            return Enum.GetValues(t).Cast<Enum>();
        }
        throw new ArgumentException("<T> must be an enumerated type.");
    }
}

In my opinion it's a little bit safer, as you can - almost - call it only with Enums, and of course instead of the throw you can simply return null if you want an exception-free version.

Pang
  • 9,564
  • 146
  • 81
  • 122
qqbenq
  • 10,220
  • 4
  • 40
  • 45
5

No.

The best you can do is put it on a static class, like this:

public static class ThingsUtils { 
    public static SelectList ToSelectList() { ... }
}
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
1

I use 'Type' instead of 'Enum' to add extension. Then I can get any type of list back from the method. Here it returns string values:

    public static string[] AllDescription(this Type enumType)
    {
        if (!enumType.IsEnum) return null;

        var list = new List<string>();
        var values = Enum.GetValues(enumType);

        foreach (var item in values)
        {
            // add any combination of information to list here:
            list.Add(string.Format("{0}", item));

            //this one gets the values from the [Description] Attribute that I usually use to fill drop downs
            //list.Add(((Enum) item).GetDescription());
        }

        return list.ToArray();
    }

Later I could use this syntax to get what I want:

var listOfThings = typeof (Things).AllDescription();
Bahram
  • 11
  • 1
1

@Aaronaught has a very good answer. To extend his answer, you can also even make it more generic. I have this in a global library...

public static IQueryable GetAllEnumValues<T>()
{
    IQueryable retVal = null;

    Type targetType = typeof(T);
    if(targetType.IsEnum)
    {
        retVal = Enum.GetValues(targetType).AsQueryable();
    }

    return retVal;
}

Now you have de-coupled this functionality from the SelectList class. So you can call this in your SelectList methods, or anywhere else for that matter.

public class SelectList
{
    public static SelectList Of<T>
    {
        IQueryable enumValues = GetAllEnumValues<T>();
        var values = 
            from Enum e in enumValues
            select new { ID = e, Name = e.ToString() };
        return new SelectList(values, "Id", "Name");
    }
}
Ted
  • 7,122
  • 9
  • 50
  • 76
undeniablyrob
  • 1,339
  • 2
  • 16
  • 16
  • Another note is that you can use the where clause to improve your compiling on generic methods for enums... GetAllEnumValues() where T : struct, IConvertible – undeniablyrob Dec 12 '11 at 18:00
0

In my opinion, this is the cleanest way. Why?

  • It works for any System.Enum
  • The extension method itself is cleaner.
  • To call it you just add new and that's a small trade off (because it has to have an instance in order to work.
  • You aren't passing null around and it literally won't compile if you try to use it with another type.

Usage:

(new Things()).ToSelectList()

Extension Method:

[Extension()]
public SelectList ToSelectList(System.Enum source)
{
    var values = from Enum e in Enum.GetValues(source.GetType)
                select new { ID = e, Name = e.ToString() };
    return new SelectList(values, "Id", "Name");    
}
toddmo
  • 20,682
  • 14
  • 97
  • 107
-1

The closest you can come, I think, is to dummy things up a bit to work like an enum without being one. Here's what I've come up with--it seems like a lot of work just to plop a static method on an enumeration, although I do understand the programming appeal of it:

    public class PseudoEnum
{
    public const int FirstValue = 1;
    private static PseudoEnum FirstValueObject = new PseudoEnum(1);

    public const int SecondValue = 2;
    private static PseudoEnum SecondValueObject = new PseudoEnum(2);

    private int intValue;

    // This prevents instantation; note that we cannot mark the class static
    private PseudoEnum() {}

    private PseudoEnum(int _intValue)
    {
        intValue = _intValue;
    }

    public static implicit operator int(PseudoEnum i)
    {
        return i.intValue;
    }

    public static implicit operator PseudoEnum(int i)
    {
        switch (i)
        {
            case FirstValue :
                return FirstValueObject;
            case SecondValue :
                return SecondValueObject;
            default:
                throw new InvalidCastException();
        }
    }

    public static void DoSomething(PseudoEnum pe)
    {
        switch (pe)
        {
            case PseudoEnum.FirstValue:
                break;
            case PseudoEnum.SecondValue:
                break;
        }
    }

}
Iucounu
  • 1,630
  • 1
  • 20
  • 31