5

I have an object. Usually it is either long or string, so to simplify the code let's assume just that.

I have to create a method that tries to convert this object to a provided enum. So:

public object ToEnum(Type enumType, object value)
{
    if(enumType.IsEnum)
    {
        if(Enum.IsDefined(enumType, value))
        {
           var val = Enum.Parse(enumType, (string)value);
           return val;
        }
    }
    return null;
}

With strings it works well. With numbers it causes problems, because a default underlying type for enum is int, not long and IsDefined throws an ArgumentException.

Of course I can do many checks, conversions or try-catches.

What I want is to have a clean and small code for that. Any ideas how to make it readable and simple?

void
  • 881
  • 3
  • 20
  • 27
Piotr Zierhoffer
  • 5,005
  • 1
  • 38
  • 59

2 Answers2

4

It feels to me like you only actually want to handle three cases:

  • Input is already the right type
  • Strings
  • Integers in various types

I believe this will do what you want for valid input:

public object ToEnum(Type enumType, object value)
{
    if (value == null)
    {
        throw new ArgumentNullException("value");
    }
    if (enumType == null)
    {
        throw new ArgumentNullException("type");
    }
    if (!enumType.IsEnum)
    {
        return false;
    }
    string valueString = value as string;
    if (valueString != null)
    {
        return Enum.IsDefined(enumType, value) ? Enum.Parse(enumType, valueString) : null;
    }
    if (value.GetType() == enumType)
    {
        return value;
    }
    // This appears to handle longs etc
    return Enum.ToObject(enumType, value);
}

However, that will return a value of the right type even for undefined values. If you don't want that, change the last part to:

object candidate = Enum.ToObject(enumType, value);
return Enum.IsDefined(enumType, candidate) ? candidate : null;

Also, this will still throw an exception if you pass in a floating point number, or something like that. If you don't want that behaviour, you'll need to have a set of all the types you do want to accept, and check against that first.

void
  • 881
  • 3
  • 20
  • 27
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • That is a perfectly good answer, unsurprisingly. Thank you, Jon! I missed this ToObject method. Actually the number of available overloads handles multiple `if`s I wanted to avoid. To be honest, it's the nth time I find that a base class for (integer) numbers would be neat ;-) Thanks again. – Piotr Zierhoffer Jan 31 '14 at 14:40
  • @PiotrZierhoffer: If you're doing a lot of work with enums, you might find my Unconstrained Melody library handy: https://code.google.com/p/unconstrained-melody/ – Jon Skeet Jan 31 '14 at 14:44
  • looks interesting, thank you! As I can see from your page it needs some post-build processing, which could be a bit troublesome on Mono. But I've hit this problems so many times... – Piotr Zierhoffer Jan 31 '14 at 15:04
  • @PiotrZierhoffer: You only need to post-process the library build - you should then be able to use it from anywhere. – Jon Skeet Jan 31 '14 at 15:06
  • Oh, I get it. Nice! I think I'll give it a try :) – Piotr Zierhoffer Jan 31 '14 at 15:10
0

Try this

public object ToEnum<T>(object value)
    {
        var type = typeof(T);

        if (type.IsEnum)
        {
            int numberVal;

            if (!int.TryParse(value.ToString(), out numberVal) && value.GetType() != typeof(string))
            {
                return null;
            }

            value = numberVal;

            if (Enum.IsDefined(type, value))
            {
                T result = (T)Enum.Parse(type, value.ToString());
                return result;
            }
        }
        return null;
    }
  • Actually it would need some tinkering to enable string values (i.e. names of fields, not only integer values). And converting int to string to int feels bad to me, like the wrong way to go... But thanks anyway :) – Piotr Zierhoffer Jan 31 '14 at 15:14