views:

289

answers:

1

I'm trying to use reflection to set properties on some OpenXML types (e.g. Justification). Assigning a value by enumerating all possibilities is straight-forward:

// attr is an XmlAttribute, so .Name and .Value are Strings
if (attr.Name == "Val")
{
    if (element is Justification)
    {
        ((Justification)element).Val = (JustificationValues)Enum.Parse(typeof(JustificationValues), attr.Value);
            return;
    }
    else
    {
        // test for dozens of other types, such as TabStop
    }
}

What makes this difficult to do via reflection is: 1) The type of the Val property is EnumValue<T>, so I don't know how to extract the type to pass as the first argument to Enum.Parse. 2) There is an implicit conversion from the actual enumeration type to the EnumValue<> type, which I don't know how to invoke with reflection.

I would like the code to end up looking something like:

PropertyInfo pInfo = element.GetType().GetProperty(attr.Name);
Object value = ConvertToPropType(pInfo.PropertyType, attr.Value); /* this 
    would return an instance of EnumValue<JustificationValues> in this case */
pInfo.SetValue(element, value, null);

How do I implement ConvertToPropType? Or is there a better solution?

Thanks

Edit: I got a solution working using Earwicker's suggestion, but it relies on the convenient fact that the enumeration's type name can be derived from the node's type name ("Justification" -> "JustificationValues"). I'm still curious how to solve this in the general case, though.

Edit2: GetGenericArguments got me the rest of the way there. Thanks.

+3  A: 

If the attribute value is just a string, I assume you already have some way of figuring out that the string identifies a value from a specific enumeration. In your example you have it hard-coded, so I'm not sure if that's what you want or what you want to change.

Assuming you know it's an enumeration, and you know which enumeration, you already know how to get an object containing a boxed value of the right enum type, as in your snippet.

Now if I assume EnumValue<T> has a constructor that takes a T.

Type genericType = typeof(EnumValue<>);
Type concreteType = genericType.MakeGenericType(typeof(JustificationValues));

Now concreteType is the type EnumValue<JustificationValues>.

From that you can get a constructor, hopefully one that takes a JustificationValues parameter, and Invoke it.

Update

Ahh, I see what you're doing now. You use the XML attribute name to pick a C# property. You need to be able to detect whether that property is of a type EnumValue<T>, and find out what T is.

PropertyInfo p = // ... get property info

Type t = p.GetType();

if (t.IsGenericType && 
    t.GetGenericTypeDefinition == typeof(EnumValue<>))
{
    Type e = t.GetGenericArguments()[0]; // get first (and only) type arg

    // e is the enum type...

Give that a try.

Daniel Earwicker
I don't know the enumeration type in general, it depends on the type of element. I didn't know about MakeGenericType but I don't think I'll be able to use it until I figure out how to extract the type T from a type EnumValue<T>
Brian
Sorry, I'd missed that you were looking up the property using the attribute name.
Daniel Earwicker