Update 5/26/2014 : Added Enum Extension to Extract Custom Attribute.

I use this trick in .Net to extensively Trojan Horse extra meta type information on any object. At run time I reflect off of the object and extract this information which was placed on it. Below I show how to do it on an enum using a built in attribute in .Net and secondly I show how to create your own custom attribute. Note: in the example is an enum, but one can decorate any object with the attribute.

Single Attribute Values

The first way which will be shown is done using C#’s description attribute found in the System.ComponentModel namespace and does not require any special coding such as creating a class definition for the attribute. The second way shows a more robust custom attribute which has more specialized information.

Step 1 Include these namespaces:

using System.Reflection;
using System.ComponentModel;

Step 2 Decorate the enums with the Description Attribute

public enum EPortalErrors
{
  [Description("Internal programming error")]
  InternalError,
  [Description("Invalid request Xml: Reset XSLT.")]
  InvalidXmlSentIn,
}

Step 3 Create a static method to divine the attribute description

/// <summary>If an attribute such as is on an enumeration exists, this will return that
/// information</summary>
/// <param name="value">The object which has the attribute.</param>
/// <returns>The description string of the attribute or string.empty</returns>
public static string GetAttributeDescription( object value )
{
    string retVal = string.Empty;
    try
    {
        FieldInfo fieldInfo = value.GetType().GetField(value.ToString());

        DescriptionAttribute[] attributes =
           (DescriptionAttribute[])fieldInfo.GetCustomAttributes
           (typeof(DescriptionAttribute), false);

        retVal = ( ( attributes.Length > 0 ) ? attributes[0].Description : value.ToString() );
    }
    catch (NullReferenceException)
    {
     //Occurs when we attempt to get description of an enum value that does not exist
    }
    finally
    {
        if (string.IsNullOrEmpty(retVal))
            retVal = "Unknown";
    }    

    return retVal;
}

Step 4 Call GetAttributeDescription with the enum in hand and we will get the extended text. Done…


Multiple Attribute Values

That first method is is nice for basic information, but what if one needs more information? That can be done by writing a custom attribute information extraction class. We will start at step 2 from above:

Step 2 Determine what items should be on the attribute and create an attribute name. In the below example we want to decorate the enum with three integers and another enum. This extended information will allow for other processing to occur which is dependant on that info. Our attribute name is DatabaseConnection which will mirror our attribute extraction class

public enum EActivities
{
   HoldingsGet,

    /// <summary>We need to inform the DAL that the primary keys are 1,2,3 and
    /// that we only want one column</summary>
   [DatabaseConnection(1,2,3, DatabaseConnectionAttribute.Columns.OneColumn)]
   OpsDBAttributionBatchCodes,
}

Step 3 Create a static method to divine the attribute description for DatabaseConnection. Note there is other activities/properties that are shown, for the consumer can get the extended int info in a list. Note also we have to decorate the class with an attribute to let the system know that this class will be used as an attribute

/// <summary>When using Enums for exceptions this attribute provides more information
/// to describe the enumeration such as target fields and primary keys.</summary>
[AttributeUsage(AttributeTargets.Field)]
public class DatabaseConnectionAttribute : Attribute
{

    #region Construction    

    /// <summary>Used to specify the needed columns.</summary>
    public enum Columns : int
    {
        OneColumn = 1,
        TwoColumns = 2,
        ThreeColumns = 3,
        FourColumns = 4,
        FiveColumns = 5,
        SixColumns = 6
    }

    /// <summary>Allow a consumer to specify an attribute with for data items.</summary>
    public DatabaseConnectionAttribute(int pkey1, int pkey2, int pkey3, DatabaseConnectionAttribute.Columns columns)
    {
        pkeys.Add(pkey1);
        pkeys.Add(pkey2);
        pkeys.Add(pkey3);
        _Columns = columns;
    }

    #endregion
    #region Properties
    /// <summary>To total number of columns to extract from the database.</summary>
    public DatabaseConnectionAttribute.Columns ColumnsNeeded
    {
        get { return _Columns; }
    }    

    /// <summary>Retrieves the list of primary keys.</summary>
    public List<int> PrimaryKeys
    {
        get { return pkeys; }
    }

    #endregion    

    #region Exposed Static Functionality

    /// <summary>This method will peer into an enum and extract this class if it exists.</summary>
    /// <param name="enumItem">The enumeration which contains the DatabaseConnectionAttribute.</param>
    /// <returns>The current object or null</returns>
    public static DatabaseConnectionAttribute ExtractAttribute(object enumItem)
    {
        DatabaseConnectionAttribute retVal = null;

        try
        {
            FieldInfo fieldInfo = enumItem.GetType().GetField(enumItem.ToString());
            DatabaseConnectionAttribute[] attributes =
                (DatabaseConnectionAttribute[])fieldInfo.GetCustomAttributes
                (typeof(DatabaseConnectionAttribute), false);

            if (attributes != null)
                if (attributes.Length > 0)
                    retVal = attributes[0];
        }
        catch (NullReferenceException)
        {
            //Occurs when we attempt to get description of an enum value that does not exist
        }

        return retVal;
    }
    #endregion

    #region Variables
    /// <summary>Holds the primary key information.</summary>
    List<int> pkeys = new List<int>();    

    /// <summary>What columns are required by the user.</summary>
    private DatabaseConnectionAttribute.Columns _Columns;
    #endregion

}

Step 4 Now by calling the static ExtractAttribute we are returned an object that has all of the information off of the enum. Quite handy.


 

Update: Extracting Custom Attribute using a Generic Extension.

Use this code to extract your custom extension. In the below example we use the custom extension to get the custom attribute, of the enum defined above and its custom attribute.

DatabaseConnectionAttribute custAttr = 

    EActivities.OpsDBAttributionBatchCodes
               .ExtractAttribute<DatabaseConnectionAttribute, EActivities>();

Extension Method Code

/// <summary>
/// If an enum has a custom attrbute, this will returrn that attribute or null.
/// </summary>
/// <typeparam name="TCustomAttr">The type of the custom attribute to extract from the enum.</typeparam>
/// <typeparam name="TEnumItance">The enum currently being viewed..</typeparam>
/// <param name="instance">The instance.</param>
/// <returns>The custom attribute (TCustomAttr) or null</returns>
public static TCustomAttr ExtractAttribute<TCustomAttr, TEnum>(this TEnum instance)
{
    if (instance != null)
    {
        try
        {
            FieldInfo fieldInfo = instance.GetType()
                                          .GetField(instance.ToString());

            var attributes = fieldInfo.GetCustomAttributes(typeof(TCustomAttr), false)
                                      .ToList();

            if (attributes.Any())
                return (TCustomAttr)attributes[0];

        }
        catch (Exception)
        {
        }
    }

    return default(TCustomAttr);
}
Share