Enum and performance

25,170

Solution 1

Casting from int to an enum is extremely cheap... it'll be faster than a dictionary lookup. Basically it's a no-op, just copying the bits into a location with a different notional type.

Parsing a string into an enum value will be somewhat slower.

I doubt that this is going to be a bottleneck for you however you do it though, to be honest... without knowing more about what you're doing, it's somewhat hard to recommendation beyond the normal "write the simplest, mode readable and maintainable code which will work, then check that it performs well enough."

Solution 2

You're not going to notice a big difference in performance between the two, but I'd still recommend using a Dictionary because it will give you a little more flexibility in the future.

For one thing, an Enum in C# can't automatically have a class associated with it like in Java, so if you want to associate additional information with a state (Full Name, Capital City, Postal abbreviation, etc.), creating a UnitedState class will make it easier to package all of that information into one collection.

Also, even though you think this value will never change, it's not perfectly immutable. You could conceivably have a new requirement to include Territories, for example. Or maybe you'll need to allow Canadian users to see the names of Canadian Provinces instead. If you treat this collection like any other collection of data (using a repository to retrieve values from it), you will later have the option to change your repository implementation to pull values from a different source (Database, Web Service, Session, etc.). Enums are much less versatile.

Edit

Regarding the performance argument: Keep in mind that you're not just casting an Enum to an int: you're also running ToString() on that enum, which adds considerable processing time. Consider the following test:

const int C = 10000;
int[] ids = new int[C];
string[] names = new string[C];
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i< C; i++)
{
    var id = (i % 50) + 1;
    names[i] = ((States)id).ToString();
}
sw.Stop();
Console.WriteLine("Enum: " + sw.Elapsed.TotalMilliseconds);
var namesById = Enum.GetValues(typeof(States)).Cast<States>()
                .ToDictionary(s => (int) s, s => s.ToString());
sw.Restart();
for (int i = 0; i< C; i++)
{
    var id = (i % 50) + 1;
    names[i] = namesById[id];
}
sw.Stop();
Console.WriteLine("Dictionary: " + sw.Elapsed.TotalMilliseconds);

Results:

Enum: 26.4875
Dictionary: 0.7684

So if performance really is your primary concern, a Dictionary is definitely the way to go. However, we're talking about such fast times here that there are half a dozen other concerns I'd address before I would even care about the speed issue.

Enums in C# were not designed to provide mappings between values and strings. They were designed to provide strongly-typed constant values that you can pass around in code. The two main advantages of this are:

  1. You have an extra compiler-checked clue to help you avoid passing arguments in the wrong order, etc.
  2. Rather than putting "magical" number values (e.g. "42") in your code, you can say "States.Oklahoma", which renders your code more readable.

Unlike Java, C# does not automatically check cast values to ensure that they are valid (myState = (States)321), so you don't get any runtime data checks on inputs without doing them manually. If you don't have code that refers to the states explicitly ("States.Oklahoma"), then you don't get any value from #2 above. That leaves us with #1 as the only real reason to use enums. If this is a good enough reason for you, then I would suggest using enums instead of ints as your key values. Then, when you need a string or some other value related to the state, perform a Dictionary lookup.

Here's how I'd do it:

public enum StateKey{
    AL = 1,AK,AS,AZ,AR,CA,CO,CT,DE,DC,FM,FL,GA,GU,
    HI,ID,IL,IN,IA,KS,KY,LA,ME,MH,MD,MA,MI,MN,MS,
    MO,MT,NE,NV,NH,NJ,NM,NY,NC,ND,MP,OH,OK,OR,PW,
    PA,PR,RI,SC,SD,TN,TX,UT,VT,VI,VA,WA,WV,WI,WY,
}

public class State
{
    public StateKey Key {get;set;}
    public int IntKey {get {return (int)Key;}}
    public string PostalAbbreviation {get;set;}
    
}

public interface IStateRepository
{
    State GetByKey(StateKey key);
}

public class StateRepository : IStateRepository
{
    private static Dictionary<StateKey, State> _statesByKey;
    static StateRepository()
    {
        _statesByKey = Enum.GetValues(typeof(StateKey))
        .Cast<StateKey>()
        .ToDictionary(k => k, k => new State {Key = k, PostalAbbreviation = k.ToString()});
    }
    public State GetByKey(StateKey key)
    {
        return _statesByKey[key];
    }
}

public class Foo
{
    IStateRepository _repository;
    // Dependency Injection makes this class unit-testable
    public Foo(IStateRepository repository) 
    {
        _repository = repository;
    }
    // If you haven't learned the wonders of DI, do this:
    public Foo()
    {
        _repository = new StateRepository();
    }
    
    public void DoSomethingWithAState(StateKey key)
    {
        Console.WriteLine(_repository.GetByKey(key).PostalAbbreviation);
    }
}

This way:

  1. you get to pass around strongly-typed values that represent a state,
  2. your lookup gets fail-fast behavior if it is given invalid input,
  3. you can easily change where the actual state data resides in the future,
  4. you can easily add state-related data to the State class in the future,
  5. you can easily add new states, territories, districts, provinces, or whatever else in the future.
  6. getting a name from an int is still about 15 times faster than when using Enum.ToString().

[grunt]

Solution 3

You could use TypeSafeEnum s

Here's a base class

Public MustInherit Class AbstractTypeSafeEnum
    Private Shared ReadOnly syncroot As New Object
    Private Shared masterValue As Integer = 0

    Protected ReadOnly _name As String
    Protected ReadOnly _value As Integer

    Protected Sub New(ByVal name As String)
        Me._name = name
        SyncLock syncroot
            masterValue += 1
            Me._value = masterValue
        End SyncLock
    End Sub

    Public ReadOnly Property value() As Integer
        Get
            Return _value
        End Get
    End Property

    Public Overrides Function ToString() As String
        Return _name
    End Function

    Public Shared Operator =(ByVal ats1 As AbstractTypeSafeEnum, ByVal ats2 As AbstractTypeSafeEnum) As Boolean
        Return (ats1._value = ats2._value) And Type.Equals(ats1.GetType, ats2.GetType)
    End Operator

    Public Shared Operator <>(ByVal ats1 As AbstractTypeSafeEnum, ByVal ats2 As AbstractTypeSafeEnum) As Boolean
        Return Not (ats1 = ats2)
    End Operator

End Class

And here's an Enum :

Public NotInheritable Class EnumProcType
    Inherits AbstractTypeSafeEnum

    Public Shared ReadOnly CREATE As New EnumProcType("Création")
    Public Shared ReadOnly MODIF As New EnumProcType("Modification")
    Public Shared ReadOnly DELETE As New EnumProcType("Suppression")

    Private Sub New(ByVal name As String)
        MyBase.New(name)
    End Sub

End Class

And it gets easier to add Internationalization.

Sorry about the fact that it's in VB and french though.

Cheers !

Share:
25,170
user3224001
Author by

user3224001

Passionate Tech Owl :)

Updated on April 27, 2021

Comments

  • user3224001
    user3224001 about 3 years

    My app has a lot of different lookup values, these values don't ever change, e.g. US States. Rather than putting them into database tables, I'd like to use enums.

    But, I do realize doing it this way involves having a few enums and a lot of casting from "int" and "string" to and from my enums.

    Alternative, I see someone mentioned using a Dictionary<> as a lookup tables, but enum implementation seems to be cleaner.

    So, I'd like to ask if keeping and passing around a lot of enums and casting them be a problem to performance or should I use the lookup tables approach, which performs better?

    Edit: The casting is needed as ID to be stored in other database tables.