Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Custom Attributes with Extension Methods: Resource Key

0.00/5 (No votes)
12 Jul 2011 2  
Using a custom attribute to contain a resource key

I picked up this technique in my last job to use a custom attribute to contain a resource key. The biggest benefit was all the enums in the system used this attribute which provided a way to translate that enum to text. Take a look at a sample enum:

public enum Mode
{
    [AttributeResourceKey("lblInvalid")]
    Invalid,
    [AttributeResourceKey("lblReview")]
    Review,
    [AttributeResourceKey("lblCheckout")]
    Checkout,
    [AttributeResourceKey("lblOrdered")]
    Ordered
}

Each enum uses the AttributeResourceKey to specify the resource key defined in the resx file. Combined with an Extension Method, we can extend the enum itself to allow us to execute the following:

public void DoOperation(Mode mode)
{
    Log.Info(GetResourceString(mode.ResourceKey()));
    ...
}

The C++ head in me thinks, “why are we using Reflection when a static function in a helper class could contain a switch statement to convert the enum to the resource key?”. Technically, this is sufficient and faster. However, the C# head in me loves the idea that the enum and the resource key are intimately tied together in the same file. There is no helper function to forget to update. The penalty of reading an attribute is a small price to pay to keep the enum and resource key together in order to increase overall maintainability.

So the first thing I am going to do is define a simple interface for my custom attributes.

public interface IAttributeValue<T>
{
    T Value { get; }
}

All this interface does is define that the custom attribute class itself will define a property called Value of type T. This will be useful when using the generic method below for pulling the attribute. Next, we define the custom attribute class itself.

public sealed class AttributeResourceKey : Attribute, IAttributeValue<string>
{
    private string _resourceKey;
    public AttributeResourceKey(string resourceKey)
    {
        _resourceKey = resourceKey;
    }

    #region IAttributeValue<string> Members
    public string Value
    {
        get { return _resourceKey; }
    }
    #endregion
}

Notice how simple the above class is. We have a constructor taking a string and a property called Value which returns the said string. Now let’s look at the generic method for pulling the attribute.

public static class AttributeHelper
{
    /// <summary>
    /// Given an enum, pull out its attribute (if present)
    /// </summary>
    public static TReturn GetValue<TAttribute, TReturn>(object value)
    where TAttribute: IAttributeValue<TReturn>
    {
        FieldInfo fieldInfo = value.GetType().GetField(value.ToString());
        object[] attribs    = fieldInfo.GetCustomAttributes(typeof(TAttribute), false);
        TReturn returnValue = default(TReturn);

        if (attribs != null && attribs.Length > 0)
            returnValue = ((TAttribute)attribs[0]).Value;

        return returnValue;
    }
}

Above is the heart of the code. It uses Generics so you need only define this code once in a static class. By passing the attribute and return type, we can extract our Value defined by IAttributeValue<TReturn>. Using the where constraint on TAttribute allows the generic method to know this type defines a property called Value of type TReturn. This exposes the true power of Generics as without this constraint, the method could only presume TAttribute is nothing more than an object. This might tempt you to wrongly cast TAttribute in order to access its properties, inviting an exception only seen at runtime.

Now to define our Extension Method, to be placed in a common namespace, to extend all enums with the ResourceKey() method.

public static class EnumerationExtensions
{
    /// <summary>
    /// Given an enum, pull out its resource key (if present)
    /// </summary>
    public static string ResourceKey(this Enum value)
    {
        return AttributeHelper.GetValue<AttributeResourceKey, string>(value);
    }
}

Thanks to the generic attribute helper, the above Extension Method looks trivial. We simply use the helper to return the resource key and now we’ve extended all our enums to have this useful property.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here