Click here to Skip to main content
12,950,618 members (59,163 online)
Click here to Skip to main content
Add your own
alternative version


81 bookmarked
Posted 5 Nov 2011

Optimized Enum.ToString()

, 6 Nov 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
A generic version of Enum, which provides much more faster formatting
This is an old version of the currently published article.


While for most projects this is not an issue, some developers might notice that simple converting enumeration value into string takes quite a lot of processor time. This is especially important if you develop a high-loaded server application and need to get enumeration names thousands sometimes per request. The class described in this articles solves the problem once for any enum.

Note that method Enum.ToString() is very often called implicitly, for example in the following statements:  

"Paint it " + Color.Black
Console.Write("Paint it {0}", Color.Black)

The Problem 

In order to find string representation of the enumeration value .NET framework uses reflection, which is known to be quite an expensive operation. Also it uses universal approach to all enumerations, checking if it was declared with FlagsAttribute and so on. It also results in excessive memory consumption, because new strings are allocated whenever ToString is called.

The simplest and quickest solution is to declare an array of strings containing string representations of all values in the enumeration. For example, for the simple enumeration

enum Color

The array will look like this

private static readonly string[] colorNames = new[] {"Red", "Green", "Blue"};

In order to convert Color value to string one should simply cast enumeration value to int and take an array value by that index:

string myColor = colorNames[(int) color]; 

Standard implementation of Enum.ToString() returns number if the value is not defined. So our code should do array bounds checking.   

int i = (int)value;
string myColor = i >= 0 && i < colorNames.Length ? colorNames[i] : i.ToString();

Actually you don't need to type the array items manually, you can simply assign it the result of Enum.GetNames() method: 

private static readonly string[] colorNames = Enum.GetNames(typeof(Color));

While this approach is really the fastest it has some limitations:

  1. It can be applied only to very simple enumerations. It won't work with "sparse" enumerations, or enumerations with FlagsAttribute attribute. If you want to improve performance of a sparse enumeration you should use a dictionary instead of the array (which is a bit less fast). Converting Flags to string is quite a complicated operation.  
  2. It requires additional coding for every enumeration you want to format.
Another popular solution is using the switch statement. It also performs very well and it supports any type of enumeration, however requires even more manual coding and keeping enumerations and formatting methods in sync.

The Solution

The goal was to create a universal method, which does exactly what Enum.ToString() does, but efficiently.

In order to generalize the approach and make it as fast as possible for any enumeration type, I made use of an interesting feature provided by generics: static constructor of a generic type is called once for each actual type used as a typed parameter and each specialization of a generic type uses its own set of static fields. So we can declare a generic Enum type and save all information required to perform enum formatting in its static members. We can also apply Strategy design pattern to select the best strategy for each enumeration type. Here is the class declaration  

/// <summary>
/// Helper class for enum types
/// </summary>
/// <typeparam name="T">Must be enum type (declared using the <c>enum</c> keyword)</typeparam>
public static class Enum<t> where T : struct, IConvertible


(I wish .NET had a constraint for enums.)

In my static class I consider three kinds of enumerations. Simple - when named constants take all consecutive values from 0 to any N.  Sparse - when named constants are not consecutive, or start not from zero (basically almost any enums with initializers). Flagged - marked with FlagsAttribute attribute. I use the array of strings to store names for simple enums, Dictionary<int,string> to store names for sparse enums, and dictionary + array of values as unsigned integers for flagged enums.

Take a look at the static constructor:

static Enum()
    Type type = typeof(T);
    if (!type.IsEnum)
        throw new ArgumentException("Generic Enum type works only with enums");
    string[] names = Enum.GetNames(type);
    var values = (T[])Enum.GetValues(type);
    if (type.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0)
        Converter = new FlagsEnumConverter(names, values);
        if (values.Where((t, i) => Convert.ToInt32(t) != i).Any())
            Converter = new DictionaryEnumConverter(names, values);
        if (Converter == null)
            Converter = new ArrayEnumConverter(names);

Classes ArrayEnumConverter, DictionaryEnumConverter and FlagsEnumConverter are nested classes derived from the single abstract class EnumConverter. Converter is a static field of type EnumConverter.

For the sake of performance class Enum supports getting enum value name not only by enumeration value, but also by the respective integer value. The corresponding methods simply call the method of the saved EnumConverter instance.

/// <summary>
/// Converts enum value to string
/// </summary>
/// <param name="value">Enum value converted to int</param>
/// <returns>If <paramref name="value"/> is defined, the enum member name; otherwise the string representation of the <paramref name="value"/>.
/// If <see cref="FlagsAttribute"/> is applied, can return comma-separated list of values</returns>
public static string ToString(int value)
    return Converter.ToStringInternal(value);

/// <summary>
/// Converts enum value to string
/// </summary>
/// <param name="value">Enum value</param>
/// <returns>If <paramref name="value"/> is defined, the enum member name; otherwise the string representation of the <paramref name="value"/>.
/// If <see cref="FlagsAttribute"/> is applied, can return comma-separated list of values</returns>
public static string ToString(T value)
    return Converter.ToStringInternal(value.ToInt32(null));

Using the methods is almost as simple as using standard implementation (although it won't be called implicitly)

string myColor = Enum<Color>.ToString(Color.Blue);
string myColor = Enum<Color>.ToString((int)Color.Blue);
string myColor = Enum<Color>.ToString(2);

What about Parse? 

Obviously the same approach can be used to parse enumeration values. The faster way to do so is to use internal Dictionary<string,int> to quickly find values by enumeration names. However as it wasn't the goal of the article in the attached source code I used the same collections as for ToString() methods and simply enumerate by them. I also added ability to restrict parsing an arbitrary integer value into undeclared enumeration value, which is not supported in the standard implementation. It also contains methods TryParse, so if you use .NET 2.0, you might find them useful.

        public static T Parse(string value, bool ignoreCase = false, bool parseNumeric = true)
            return (T) Enum.ToObject(typeof(T), Converter.ParseInternal(value, ignoreCase, parseNumeric));

        public static bool TryParse(string value, bool ignoreCase, bool parseNumeric, out T result)
            int ir;
            bool b = Converter.TryParseInternal(value, ignoreCase, parseNumeric, out ir);
            result = (T) Enum.ToObject(typeof(T), ir);
            return b;

        public static bool TryParse(string value, bool ignoreCase, out T result)
            int ir;
            bool b = Converter.TryParseInternal(value, ignoreCase, true, out ir);
            result = (T)Enum.ToObject(typeof(T), ir);
            return b;

        public static bool TryParse(string value, out T result)
            int ir;
            bool b = Converter.TryParseInternal(value, false, true, out ir);
            result = (T)Enum.ToObject(typeof(T), ir);
            return b;

Therefore class EnumConverter is declared as follows: 

abstract class EnumConverter
    public abstract string ToStringInternal(int value);
    public abstract int ParseInternal(string value, bool ignoreCase, bool parseNumber);
    public abstract bool TryParseInternal(string value, bool ignoreCase, bool parseNumber, out int result);


I wrote a simple performance test to compare performance of my class and standard .NET implementation. In the test I used these three enumerations:

enum Simple

enum Sparse
    MinusOne = -1,
    One = 1,
    Three = 3,
    Hundred = 100,

enum Flagged
    None = 0,
    One = 1,
    Two = 2,
    Both = 3,
    Four = 4,
    All = 7

In the test I simply called different implementations of the same methods 1,000,000 times and wrote the results to console. 

Here is the results of converting value Simple.Four to string using five different approaches.

Operation                                                Time  Ratio Result
Simple.Four.ToString()                                   2059   1,00 Four
Enum<Simple>.ToString(Simple.Four)                        343   6,00 Four
Enum<Simple>.ToString(4)                                   26  79,19 Four
SimpleToStringUsingSwitch(Simple.Four)                     13 158,38 Four
SimpleToStringUsingArray(Simple.Four)                      16 128,69 Four

The results show that manual approaches are still the best by performance, however our overload that accepts integer is a close runner-up and work almost 80 times faster than the standard version! The overload that accepts enumeration works 6 times faster, which is also much better.

Column Result shows that each approach produce the same result.

Test results for other values of arguments and other enumeration types:  

Operation                                                Time  Ratio Result
((Simple) 404).ToString()                                2096   1,00 404
Enum<Simple>.ToString((Simple) 404)                       618   3,39 404
Enum<Simple>.ToString(404)                                285   7,35 404
Sparse.Four.ToString()                                   2007   1,00 Four
Enum<Sparse>.ToString(Sparse.Four)                        382   5,25 Four
Enum<Sparse>.ToString(4)                                   94  21,35 Four
((Sparse) 404).ToString()                                2162   1,00 404
Enum<Sparse>.ToString((Sparse) 404)                       688   3,14 404
Enum<Sparse>.ToString(404)                                342   6,32 404
Flagged.Four.ToString()                                  5011   1,00 Four
Enum<Flagged>.ToString(Flagged.Four)                      388  12,91 Four
Enum<Flagged>.ToString(4)                                  99  50,62 Four
((Flagged) 404).ToString()                               5585   1,00 404
Enum<Flagged>.ToString((Flagged) 404)                    1138   4,91 404
Enum<Flagged>.ToString(404)                               800   6,98 404
(Flagged.Two|Flagged.Four).ToString()                    5301   1,00 Two, Four
Enum<Flagged>.ToString(Flagged.Two|Flagged.Four)         1302   4,07 Two, Four
Enum<Flagged>.ToString(6)                                 949   5,59 Two, Four
Flagged.All.ToString()                                   5021   1,00 All
Enum<Flagged>.ToString(Flagged.All)                       398  12,62 All
Enum<Flagged>.ToString(7)                                  99  50,72 All

I didn't optimize parsing methods very much, however they also performed faster than the standard implementation in most cases.

Operation                                                Time  Ratio Result
Enum.Parse(typeof(Simple), "Four")                       1191   1,00 Four
Enum<Simple>.Parse("Four")                                670   1,78 Four
Enum.Parse(typeof(Simple), "4")                          1193   1,00 Four
Enum<Simple>.Parse("4")                                   791   1,51 Four
Enum.Parse(typeof(Sparse), "Four")                       1126   1,00 Four
Enum<Sparse>.Parse("Four")                                799   1,41 Four
Enum.Parse(typeof(Sparse), "4")                          1184   1,00 Four
Enum<Sparse>.Parse("4")                                   806   1,47 Four
Enum.Parse(typeof(Flagged), "Four")                      1184   1,00 Four
Enum<Flagged>.Parse("Four")                              1114   1,06 Four
Enum.Parse(typeof(Flagged), "4")                         1229   1,00 Four
Enum<Flagged>.Parse("4")                                  948   1,30 Four
Enum.Parse(typeof(Flagged), "Two,Four")                  1517   1,00 Two, Four
Enum<Flagged>.Parse("Two,Four")                          1835   0,83 Two, Four

Despite the class uses Int32 internally it works well with any underlying type except Int64 and UInt64. This is a certain limitation, which can be easily got over, however I personally have never needed to use an enumeration of that size, so I didn't implement it.

Source Code   

In the attachment you will find the source code of the generic Enum class and the performance test. Any thought on the implementation will be very much appreciated.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Team Leader
Canada Canada
No Biography provided

You may also be interested in...

Comments and Discussions

Discussions posted for the Published version of this article. Posting a message here will take you to the publicly available article in order to continue your conversation in public.
QuestionGood article ... additional extension method Pin
Enrique Albert10-Feb-13 23:20
memberEnrique Albert10-Feb-13 23:20 
GeneralRe: Good article ... additional extension method Pin
ideafixxxer11-Feb-13 0:12
memberideafixxxer11-Feb-13 0:12 
GeneralMy vote of 5 Pin
Edo Tzumer10-Jan-13 5:05
memberEdo Tzumer10-Jan-13 5:05 
GeneralMy vote of 5 Pin
Addy Tas5-Feb-12 7:26
memberAddy Tas5-Feb-12 7:26 
QuestionGreat job Pin
Tom Clement22-Dec-11 14:28
memberTom Clement22-Dec-11 14:28 
GeneralRe: Great job Pin
ideafixxxer22-Dec-11 23:38
memberideafixxxer22-Dec-11 23:38 
Questionvery nice Pin
CIDev14-Dec-11 10:00
memberCIDev14-Dec-11 10:00 
GeneralRe: very nice Pin
ideafixxxer15-Dec-11 4:10
memberideafixxxer15-Dec-11 4:10 
GeneralMy vote of 5 Pin
linuxjr17-Nov-11 13:52
memberlinuxjr17-Nov-11 13:52 
GeneralMy vote of 5 Pin
Marc Clifton13-Nov-11 2:05
protectorMarc Clifton13-Nov-11 2:05 
GeneralMy vote of 5 Pin
Philip Liebscher7-Nov-11 12:59
memberPhilip Liebscher7-Nov-11 12:59 
Question"(I wish .NET had a constraint for enums.)" Pin
springy766-Nov-11 23:42
memberspringy766-Nov-11 23:42 
AnswerRe: "(I wish .NET had a constraint for enums.)" Pin
kornman007-Nov-11 5:24
memberkornman007-Nov-11 5:24 
QuestionAnother idea Pin
Robert Rohde6-Nov-11 9:21
memberRobert Rohde6-Nov-11 9:21 
AnswerRe: Another idea Pin
ideafixxxer6-Nov-11 10:01
memberideafixxxer6-Nov-11 10:01 
GeneralMy vote of 5 Pin
Eugene Sadovoi6-Nov-11 6:29
memberEugene Sadovoi6-Nov-11 6:29 
QuestionWhat about Int64 Pin
kornman006-Nov-11 2:46
memberkornman006-Nov-11 2:46 
AnswerRe: What about Int64 Pin
ideafixxxer6-Nov-11 6:05
memberideafixxxer6-Nov-11 6:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.170525.1 | Last Updated 6 Nov 2011
Article Copyright 2011 by ideafixxxer
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid