0

This is what I have:

    private IEnumerable<SelectListItem> GetAllEnumValues(Enum enumValues)
    {
        var allEnumValues = new List<SelectListItem>();
        var values = Enum.GetValues(enumValues.GetType());
        foreach (var selectedItem in values)
        {
            allEnumValues.Add(new SelectListItem
                                  {
                                      Value = ((int)selectedItem).ToString(),
                                      Text = selectedItem.ToString()
                                  });
        }

        return allEnumValues.AsEnumerable();
    }

This is how the method is called:

AllAgencies = GetAllEnumValues((AgencyCodes) 0)

Build is fine, but when the method is actually used, I get "Specified cast is not valid." yellow screen of death on the .Add line. I need the Value of the SelectListItem to be the actual enum number, just casted to string. So far I tried all of these: C# Iterating through an enum? (Indexing a System.Array), Enum in C# and foreach, How do I enumerate an enum? and in the end I always end up with the same error. I'm using .NET 4.0.

Community
  • 1
  • 1
Fat Shogun
  • 849
  • 1
  • 10
  • 17
  • 1
    What is the underlying type of the enum here? i.e. can you show the corresponding `enum` you are using? i.e. `AgencyCodes` ? Also: note that casting to `Enum` is a *boxing* operation - you could do the same thing with generics without needing boxing - or, since you don't really use `enumValues` except for `GetType()` - just pass in a `Type` – Marc Gravell Aug 14 '13 at 11:30
  • Value = ((int)selectedItem).ToString() if you really want to assign a string then why not this selectedItem.ToString()? and if it is interger than why not this Value = (int)selectedItem; – Ehsan Aug 14 '13 at 11:34
  • @EhsanUllah `selectedItem.ToString()` will give you the *names*; I think the OP wants the *numbers* – Marc Gravell Aug 14 '13 at 11:36
  • @MarcGravell that is why i have given the second option. that if he needs number than (int)selectedItem should be enough – Ehsan Aug 14 '13 at 11:37
  • @EhsanUllah no, the second option is simply a restatement of the thing that is *already failing*; if it fails now, it will continue to fail *then* – Marc Gravell Aug 14 '13 at 11:38
  • @Marc Gravell: That's the thing, some of them have a byte type, some of them short, and possibly even other in the future. But this helps anyway, maybe I'll try and get the enum type and pass it as a parameter in the method as well. – Fat Shogun Aug 14 '13 at 11:38
  • @EnsahUllah The problem is that I need to use a SelectListItem, and the parameter "Value" of it is an int. – Fat Shogun Aug 14 '13 at 11:40
  • @FatShogun see my answer then – Ehsan Aug 14 '13 at 11:44

3 Answers3

3

That usually means that your enum is not based on int, for example:

enum AgencyCodes : short {
    ...
}

You can't unbox an enum to the wrong type of value.

Marc Gravell
  • 927,783
  • 236
  • 2,422
  • 2,784
  • I took the easy way out and just changed the type of every enum to byte, otherwise I fear I would have needed more code, probably unnecessary and painful... – Fat Shogun Aug 14 '13 at 11:57
  • @FatShogun the other option is to switch on the `Enum.GetUnderlyingType` – Marc Gravell Aug 14 '13 at 12:02
  • It isn't immediately obvious where the boxing and unboxing is happening. Perhaps you should add that the failure is equivalent to `(int)(object)(long)0` - with the cast to `object` happening because `Enum.GetValues()` returns the non-generic `Array` type, which treats its members as `object`. – RoadieRich Aug 14 '13 at 12:30
1

If you want the numeric value, you can use Convert.ToInt32(selectedItem) and then do a ToString() on it. This if the enum is based on an int. (technically it will work unless there is a value of the enum that is too much big for the int)

And now, thanks to the magic of Expression trees... Two classes:

public static class EnumToString<T>
{
    public static readonly Func<T, string> Do;

    static EnumToString()
    {
        Type type = typeof(T);

        if (!type.IsEnum)
        {
            throw new ArgumentException();
        }

        Type baseType = type.GetEnumUnderlyingType();

        var par1 = Expression.Parameter(typeof(T));

        var cast = Expression.Convert(par1, baseType);

        Expression<Func<object, string>> toString = p => p.ToString();
        var body = (MethodCallExpression)toString.Body;

        var toString2 = Expression.Call(cast, body.Method);

        Do = Expression.Lambda<Func<T, string>>(toString2, par1).Compile();
    }
}

public static class EnumToObject<T>
{
    public static readonly Func<T, object> Do;

    static EnumToObject()
    {
        Type type = typeof(T);

        if (!type.IsEnum)
        {
            throw new ArgumentException();
        }

        Type baseType = type.GetEnumUnderlyingType();

        var par1 = Expression.Parameter(typeof(T));

        var cast1 = Expression.Convert(par1, baseType);
        var cast2 = Expression.Convert(cast1, typeof(object));

        Do = Expression.Lambda<Func<T, object>>(cast2, par1).Compile();
    }
}

static void Main()
{
    string str = EnumToString<MyEnum>.Do(MyEnum.Foo); // "1"
    object obj = EnumToObject<MyEnum>.Do(MyEnum.Foo); // (int)1
}

The first one, EnumToString<> takes the value of an enum value and convert it to string. The second one converts the value of an enum value to its base type (whatever it's) and returns an object (so it boxes the base value) (on which you can do a .ToString(), for example).

To use them you would have to change your class to something like:

IEnumerable<SelectListItem> GetAllEnumValues<T>()
{
    // In truth, the array returned by Enum.GetValues **is** strongly typed
    // but is "downcasted" to Array. So we re-upcast it.
    T[] values = (T[])Enum.GetValues(typeof(T));
    ...

        Value = EnumToString<T>.Do(selectedItem),

        // or

        Value = EnumToObject<T>.Do(selectedItem).ToString(),
}

But note that all of this is nearly useless, because there is the special formatter d:

MyEnum enu = MyEnum.Something; // (where MyEnum.Something == 1)
string str = enu.ToString("d"); // 1

Enum enu2 = enu;
string str2 = enu2.ToString("d"); // 1

See Enumeration Format Strings

xanatos
  • 102,557
  • 10
  • 176
  • 249
0

from the information that you have given i suspect that

Value = ((int)selectedItem).ToString()

is causing your problem. It should be

Value = (int)selectedItem; //if you need number and value is of type int

or

Value = selectedItem.ToString(); //if you need text and value is of type string
Ehsan
  • 28,801
  • 6
  • 51
  • 61
  • As I read it, the OP wants to get the numeric values, as strings; `selectedItem.ToString()` will give the *name*; `(int)selectedItem` is the thing that is already causing the exception - so I'm not sure this achieves the aim – Marc Gravell Aug 14 '13 at 11:37
  • @MarcGravell it could be that Value is an int type and actually conversion to string is throwing the cast exception. – Ehsan Aug 14 '13 at 11:39