Question

I have a method that reads some XML and populates an object. This is done via reflection based on the element names in the XML mathing the property names on the object. This works for basic object types but i am struggling with enum arrays.

So I have a PropertyInfo object (lets call it property) for my enum array property and I have a string value (lets call it value) containing comma separated numbers representing the enum values (e.g. "1,3,5").

I have tried:

property.SetValue(this, value.Split(',').Select(i => int.Parse(i)).ToArray(), null);

and

property.SetValue(this, value.Split(',').Select(i => Enum.ToObject(property.PropertyType.GetElementType(), int.Parse(i))).ToArray(), null);

But no joy. In the first code example the result of the Select.ToArray is an int[] which then throws a parse error. Similar case with the second but the Select.ToArray returns an object[] and again throws a parse error.

I want to write this as if the enum type is unknown.

Any ideas?

Was it helpful?

Solution

try this:

// create the enum values as object[]
var values = value.Split(',')
  .Select(i => Enum.Parse(property.PropertyType.GetElementType(), i))
  .ToArray()

// create array of correct type
Array array = (Array)Activator.CreateInstance(property.PropertyType, values.Length);

// copy the object values to the array
int i = 0;
foreach(var value in values)
{
  array.SetValue(value, i++);
}

// set the property
property.SetValue(this, array, null)

There might be a simpler way, but the key is that you create an array of the correct type.

OTHER TIPS

Here is how to do it 'correctly'. Dealing with arrays via reflection is a bit ugly:

// assume that property is an array of some enum 

var enumType = property.PropertyType.GetElementType();
var values = value.Split(',');
var enumArray = Array.CreateInstance(enumType, values.Length);
for (int i = 0; i < enumArray.Length; ++i)
    enumArray.SetValue(Enum.Parse(enumType, values[i]), i);

property.SetValue(x, enumArray);

The main point is that you need to create the array with the correct element type. If you just use ToArray, then at best you will get an int[] and at worst an object[].

You can actually set an int[] array to an enum array property (as long as the enum has int as its underlying type). This is just a slight alteration of your first example, you don't need to supply the third argument:

property.SetValue(x, value.Split(',').Select(i => int.Parse(i)).ToArray());

However, the array set to this property will then have the 'wrong' type. I'm not sure if this will actually cause any issues, but you may run into some if you're doing a lot of reflection that relies on runtime types. (This also won't work if you want to parse named enum values.)

An alternative way of doing this, using a generic method:

public class Class2
{
    public enum TestEnum
    {
        One = 1,
        Two = 2,
        Three = 3
    }

    public TestEnum[] TestValues { get; set; }

    public static T[] ConvertToEnum<T>(IEnumerable<int> values)
    {
        return values
            .Select(v => (T)Enum.ToObject(typeof(T), v))
            .ToArray();
    }

    public void Test()
    {
        var property = this.GetType().GetProperty("TestValues");

        var value = "1,2,3";

        var convert = this.GetType()
            .GetMethod("ConvertToEnum")
            .MakeGenericMethod(property.PropertyType.GetElementType());

        var intValues = value.Split(',').Select(v => int.Parse(v));

        var result = convert.Invoke(null, new[] { intValues });

        property.SetValue(this, result, null);
    }
}

The main point is to make the array creation more readable. Bonus: the ConvertToEnum<T> method could be re-used somewhere else.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top