Nerdy tidbits from my life as a software engineer

Tuesday, November 25, 2008

Reading Metadata From Enumerations

Say you define an enumeration and want to associate some metadata with its values:

/// <summary>
/// This enumeration holds all of the properties and their identifiers
/// </summary>
public enum EPropertyIDs
{
    [CustomAttribute]
    SomePropertyID1,

    [CustomAttribute]
    SomePropertyID2,

    [CustomAttribute]
    SomePropertyID3,

}

You might think it was easy to grab these values off of a value of an EPropertyID, so you’d probably do it in a similar way to how you’d read any other metadata attribute:

/// <summary>
/// Reads the CustomAttribute associated with an EPropertyID
/// </summary>
/// <param name="propertyID">The property ID to read metadata</param>
/// <returns>An instance of CustomAttribute</returns>
public static CustomAttribute ReadMetadata(EPropertyIDs propertyID)
{            
    Type t = propertyID.GetType();
    object []propertyAttributes = t.GetCustomAttributes(typeof(PropertyDeclarationAttribute), true);

    if (propertyAttributes.Length == 0)
        throw new ArgumentException("The specified property ID does not define CustomAttribute");

    return (CustomAttribute)propertyAttributes[0];

}

But this wouldn’t do what you were probably expecting.  That’s because the type of propertyID is EPropertyIDs – not the actual value of the enumeration.  To get the metadata associated with an actual value of an enumeration, we need to get the actual field that defines that enumeration value.  To understand what I mean, check out an enumeration in ILDasm – you’ll see that an enumeration is compiled as a struct with a number of static fields.  If you want to read any metadata compiled into that enum, you’ll have to use reflection to grab the actual field that defines the value.  Then you can read the custom attribute:

/// <summary>
/// Reads the CustomAttribute associated with an EPropertyID
/// </summary>
/// <param name="propertyID">The property ID to read metadata</param>
/// <returns>An instance of CustomAttribute</returns>
public static CustomAttribute ReadMetadata(EPropertyIDs propertyID)
{            
    Type enumerationType = propertyID.GetType();
    FieldInfo fieldInfo = enumerationType.GetField(propertyID.ToString());
    object[] propertyAttributes = fieldInfo.GetCustomAttributes(typeof(CustomAttribute), true);

    if (propertyAttributes.Length == 0)
        throw new ArgumentException("The specified property ID does not define CustomAttribute");

    return (CustomAttribute)propertyAttributes[0];

}

This is a lot more cumbersome than it probably should be.  But there are cases where it’s nice to associate metadata with an enumeration value, and at least this allows you to read those data and act on them.

0 comments: