3

I was trying to store and retrieve additional information from enums. I ended up with 2 methods for it. The first method is by using custom attributes. https://stackoverflow.com/a/22054994/5078531 https://stackoverflow.com/a/35040378/5078531

public class DayAttribute : Attribute
{
    public string Name { get; private set; }

    public DayAttribute(string name)
    {
        this.Name = name;
    }
}

enum Days
{
    [Day("Saturday")]
    Sat,
    [Day("Sunday")]
    Sun
}

public static TAttribute GetAttribute<TAttribute>(this Enum value)
        where TAttribute : Attribute
    {
        var enumType = value.GetType();
        var name = Enum.GetName(enumType, value);
        return enumType.GetField(name).GetCustomAttributes(false).OfType<TAttribute>().SingleOrDefault();
    }

The second method I found here, by writing an extension method on enums. https://stackoverflow.com/a/680515/5078531

public enum ArrowDirection
{
    North,
    South,
    East,
    West
}

public static class ArrowDirectionExtensions
{
     public static UnitVector UnitVector(this ArrowDirection self)
     {         
         switch(self)
         {
             case ArrowDirection.North:
                 return new UnitVector(0, 1);
             case ArrowDirection.South:
                 return new UnitVector(0, -1);
             case ArrowDirection.East:
                 return new UnitVector(1, 0);
             case ArrowDirection.West:
                 return new UnitVector(-1, 0);
             default:
                 return null;
         }
     }
}

If am looking for the performance which method should I choose ? or is there any other efficient methods which am missing?

Community
  • 1
  • 1
rebornx
  • 407
  • 6
  • 21
  • 3
    Both are actually bad. If you want a struct with predefined values, use a struct with predefined values, eg with static readonly fields that hold the predefined values. If you want to *prevent* people from creating their own instances, make the constructor private – Panagiotis Kanavos Jul 05 '16 at 10:25
  • 1
    "If am looking for the performance which method should I choose" : Neither. First you should define your performance needs (for production work, "as fast as possible" is rarely an acceptable definition), then you should pick the most readable method. Then you should benchmark. If this specific part of your code is a benchmark, you should either change it or, more likely, change the calling code to call into it less often. – Brian Jul 06 '16 at 13:01

2 Answers2

3

Both are valid ways of implementing it; as are many other ways. Personally, I like the convenience of the first. The performance penalty of reflection can be mitigated by processing the attributes once only, and storing (presumably in a static field) - potentially as a flat array if the enum values are contiguous and 0-based; otherwise, perhaps in a dictionary.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
0

You can make DayaAttributeCache more generic if you like such that it can store other enum and attribute types. I just did this quick to show a caching approach. Enum values have to be continuous starting at 0 but this can be changed to handle that case if needed.

public static class DaysAttributeCache
{
    private static readonly string[] Cache;

    static DaysAttributeCache()
    {
        Type enumType = typeof(Days);
        Cache = new string[Enum.GetValues(enumType).Length];

        foreach (Enum value in Enum.GetValues(enumType))
        {
          var name = Days.GetName(enumType, value);

          DayAttribute attribute = enumType
              .GetField(name)
              .GetCustomAttributes(false)
              .OfType<DayAttribute>()
              .SingleOrDefault();

          string weekDay = attribute?.Name;
          Cache[((IConvertible)value).ToInt32(CultureInfo.InvariantCulture)] = weekDay;
        }
    }

    public static string GetWeekday(this Days value)
    {
        return Cache[(int)value];
    }
  }

Called like this...

string saturday = Days.Sat.GetWeekday();
Trevor Ash
  • 623
  • 4
  • 8