Simulate properties for Enumerations in the .NET Framework 3.5

    I am sure that many periodically encountered the need to specify any simple properties for enumeration elements in C #.

    There are various ways to solve this problem, the most popular of which, in my opinion, is to write an auxiliary static method that takes an enumeration as a parameter and returns the result of the desired type.

    I want to offer you a slightly different, more universal and elegant way, based on attributes, methods of expansion and reflection.

    Attributes provide an efficient method for associating declarative information with C # code (types, methods, properties, etc.). An attribute associated with a program entity can be requested at runtime using a method called reflection.
    © MSDN, http://msdn.microsoft.com/en-us/library/z0w1kczw.aspx

    Reflection (reflection) is used to dynamically create a type instance, bind a type to an existing object, as well as obtain a type from an existing object and dynamically call it methods or access to its fields and properties.
    © MSDN, http://msdn.microsoft.com/en-us/library/ms173183.aspx

    Extension Methodsallow you to “add” methods to existing types without creating a new derived type, recompiling, or otherwise changing the original type. Extension methods are a special kind of static method, but they are invoked as if they were instance methods in an extended type.
    © MSDN, http://msdn.microsoft.com/en-us/library/bb383977.aspx


    Take a look at how clearly we can describe attributes for enumeration elements:

    public enum Womans
     {
      [Age(25)]
      [Weight(54.5)]
      Masha,

      [Age(32)]
      [Weight(102.5)]
      Lena,

      [Age(44)]
      [Weight(77.4)]
      Ira,

      [Age(28)]
      [Weight(63.75)]
      Fekla
     }

     public enum Status
     {
      [RussianName("Открыт")]
      Opened = 100,

      [RussianName("Закрыт")]
      Closed = 200,

      [RussianName("Что-то еще")]
      AnythingElse = 500
     }

    ... and how simple we can get the values ​​of these elements:

    double iraWeight = Womans.Ira.GetWeight();
    int lenaAge = Womans.Lena.GetAge();
    string closedName = Status.Closed.GetRussianName();


    Implementation



    Let's create an abstract BaseAttribute class , from which our own attributes will be inherited in the future. The constructor accepts and places the object for storage. The GetValue () method returns a stored object.

    public abstract class BaseAttribute : Attribute
    {
     private readonly object _value;
     public BaseAttribute(object value) { this._value = value; }

     public object GetValue() { return this._value; }
    }

    Next, we need to create an extension method, which for the element of any enumeration will return the value of the required attribute. To do this, create a static class EnumAttributesBaseLogic and define our method in it, which we will call GetAttributeValue . The last argument to the method is the value that will be returned if the passed enumeration element is not marked with the passed attribute.

    public static class EnumAttributesBaseLogic
    {
     public static VAL GetAttributeValue(this ENUM enumItem, Type attributeType, VAL defaultValue)
     {
      var attribute = enumItem.GetType().GetField(enumItem.ToString()).GetCustomAttributes(attributeType, true)
       .Where(a => a is BaseAttribute)
       .Select(a => (BaseAttribute)a)
       .FirstOrDefault();

      return attribute == null ? defaultValue : (VAL)attribute.GetValue();
     }
    }

    That's all! Now we can create the attributes we need in just one line. Take a look at how to simply create the Weight * attribute :

    public class Weight : BaseAttribute { public Weight(double value) : base(value) { } }

    * Remember that only constants or arrays of primitive types can be used as attribute arguments .

    Also, we can simply now create an extension method to read the value of the attribute we just created:

    public static double GetWeight(this Enum enumItem)
    {
     return enumItem.GetAttributeValue(typeof(Weight), 0m);
    }

    For convenience, I recommend putting your own attribute definitions and a static class with advanced methods for reading attributes into a separate file:

    using System;

    namespace EnumAttributesDemo
    {
     public class Age : BaseAttribute { public Age(int value) : base(value) { } }
     public class Weight : BaseAttribute { public Weight(double value) : base(value) { } }
     public class RussianName : BaseAttribute { public RussianName(string value) : base(value) { } }

     public static class EnumExtensionMethods
     {
      public static int GetAge(this Womans enumItem)
      {
       return enumItem.GetAttributeValue(typeof(Age), 0);
      }

      public static double GetWeight(this Womans enumItem)
      {
       return enumItem.GetAttributeValue(typeof(Weight), 0d);
      }

      public static string GetRussianName(this Status enumItem)
      {
       return enumItem.GetAttributeValue(typeof(RussianName), string.Empty);
      }
     }
    }

    A working example can be downloaded from here:
    http://www.googman.ru/sources/enumattributesdemo.zip (9.1 kb)

    PS Do not judge strictly - my first post.

    Also popular now: