Thursday, August 19, 2010

#3 Enum.ToString()

It's kinda surprise, but Enum.ToString() uses reflection to get a string representation of an enum:
public override string ToString()
{
  Type type = base.GetType();
  object obj2 = ((RtFieldInfo) GetValueField(type)).InternalGetValue(this, false);
  return InternalFormat(type, obj2);
}


* This source code was highlighted with Source Code Highlighter.
This approach leads to visible performance issues when an application widely uses enum.ToString() (Obviously, any enum).

This issue could be fixed with the following trick code:
static class EnumHelper
{
  static class Cache<T>
  {
    internal static readonly Dictionary<T, string> Values = new Dictionary<T, string>();
  }
    
  public static string FastToString<T>(this T @enum) where T:struct
  { 
    if (!typeof(T).IsEnum)
      throw new ArgumentException(string.Format("Type {0} is not an enumeration.", typeof(T)));
    lock (Cache<T>.Values)
    {
      string result;
      if (!Cache<T>.Values.TryGetValue(@enum, out result))
      {
        result = @enum.ToString();
        Cache<T>.Values.Add(@enum, result);
      }
      return result;
    }
  }
}

* This source code was highlighted with Source Code Highlighter.
And, of course, all enum.ToString() should be replaced with enum.FastToString().

6 comments:

  1. если сделать через статический конструктор, lock не нужен

    ReplyDelete
  2. В смысле - в статик конструкторе забивать все значения? Да, тогда лок не нужен, но кэш не будет Lazy.

    ReplyDelete
  3. А зачем он lazy?

    ReplyDelete
  4. Hi! In the following article I tried to do pretty much the same optimization, but make it work for more cases and optimize some more. http://www.codeproject.com/KB/dotnet/enum.aspx

    ReplyDelete
  5. Replies
    1. As far as Cache values are used under lock, this solution is thread safe.

      Delete