EAMann
EAMann

Reputation: 4146

Are custom attributes for Enums dangerous?

I am building an application that makes heavy use of Enums for custom data. Essentially, an object is stored in the database with about 28 separate attributes. Each attribute is a two-character field that's translated from the SQL straight over to an Enum.

Unfortunately, I need to also translate these values into two different human-readable values. One for a legend on a data table, and one for a CSS class to style an image on the web application front-end.

To do this, I've set up two custom attributes and applied them to the Enum where necessary. For example:

Custom Attribute Interface

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

Example Custom Attribute

public sealed class AbbreviationAttribute: Attribute, IAttribute<string>
{
    private readonly string value;

    public AbbreviationAttribute(string value)
    {
        this.value = value;
    }

    public string Value
    {
        get { return this.value; }
    }
}

Method to Retrieve Custom Attribute from Enum

public static R GetAttributeValue<T, R>(IConvertible @enum)
{
    R attributeValue = default(R);

    if (@enum != null)
    {
        FieldInfo fi = @enum.GetType().GetField(@enum.ToString());

        if (fi != null)
        {
            T[] attributes = fi.GetCustomAttributes(typeof(T), false) as T[];

            if (attributes != null && attributes.Length > 0)
            {
                IAttribute<R> attribute = attributes[0] as IAttribute<R>;

                if (attribute != null)
                {
                    attributeValue = attribute.Value;
                }
            }
        }
    }

    return attributeValue;
}

Example Enum Using This Pattern

public enum Download
{
    [Abbreviation("check")]
    [Description("Certified")]
    C = 1,

    [Abbreviation("no-formal")]
    [Description("No formal certification")]
    NF = 2,

    [Abbreviation("cert-prob")]
    [Description("Certified with potential problems")]
    CP = 3
}

Both Abbreviation and Description are custom attributes that implement IAttribute<T>. My actual Enum has 11 possible values, and as I mentioned before it's used in 28 separate properties in my custom object. Using custom attributes seemed the best way to map this information back and forth.

Now for the question, is this the best way to accomplish this? I store the Enum value ("C", "NF", or "CP" in the snippet above) in the database, but I need the values of the Abbreviation and Description in my code. Also, I doubt this will be the final set of custom attributes I'll need.

Before I keep moving forward with this pattern ... is it the right way to do things? I'd rather fix potential problems with this approach now than have to track back and refactor later.

Upvotes: 28

Views: 14781

Answers (3)

Sam Axe
Sam Axe

Reputation: 33738

This is the same method I use. The one downside is serialization. The custom attributes values do not serialize.

I like the custom attribute method over the database method because it ties the attribute data right to the enum instead of having to use a lookup table or class, etc.

Upvotes: 16

djdanlib
djdanlib

Reputation: 22496

I'd probably build a hash table and a special type for something like this. You may have already discarded the idea for some reason or another, but here's what I would do not knowing the specifics of your application.

class SpecialType {
  // include the fields and all attributes that you need to reference, ToString method for debugging, and any serialization you need
  public string foo { get; set; }
  public string bar { get; set; }
  public ToString() { return "SpecialType with foo '" + foo + "' and bar '" + bar + "'"; }
}

Dictionary<int, SpecialType> myDict = new Dictionary<int, SpecialType> {
   { 1, new SpecialType { foo = "XA1B2", bar = "XC3D4" } },
   { 2, new SpecialType { foo = "ZA1B2", bar = "ZC3D4" } },
   { 3, new SpecialType { foo = "YA1B2", bar = "YC3D4" } },
}

Then I could easily keep ints in my other classes to save memory, find out if a particular value was valid by checking for existence in the Keys of the Dictionary, all that jazz. It would probably be a lot easier to do databinding if you're eventually going to use WPF or read/write to disk, too.

Upvotes: 2

eouw0o83hf
eouw0o83hf

Reputation: 9588

Can you alter the database? I think the best option would be to make a table (or tables) to house the possible values of the enums and foreign key the main objects over to it (instead of using char codes - this makes pulling it in easier and normalizes your DB). Give the table an Abbreviation and Description column, then pull those in and reference them by their key, and cache them if lookups are slow.

One thing that's dangerous about the attributes is that if any of those strings ever have to change, it's a complete redeploy of the app. If you make them database values, you can change them with a simple UPDATE.

Upvotes: 1

Related Questions