Enum and performance
Asked Answered
H

8

33

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.

Halfcocked answered 15/7, 2010 at 14:46 Comment(6)
Why will you need to cast them?Transudation
@LukeH: Presumably because in the database the values will just be integers.Hydro
@Jon: That's what I guessed, but it'd be good to get clarification from the OP.Transudation
Ray, please let me know what you think of my updated answer.Wernsman
@StriplingWarrior: Thank you so much for your help, I've upvoted your answer. I've decided to use both approach, use enums for int, and use a dictionary for string lookup.Halfcocked
I know this is an old question but "these values don't ever change, e.g. US States" is a pretty mighty assumption. Who is to say the US won't ever gain or loose states? Alaska and Hawaii weren't US states until 1959 - less than a century ago.Maffa
H
51

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."

Hydro answered 15/7, 2010 at 14:50 Comment(2)
This might be another question altogether but related: is casting enums to each other with matching underlying int values just as cheap as the (int) cast?Bosnia
@CADBloke: I'd expect so, yes. But try if it's important to you :)Hydro
W
20

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]

Wernsman answered 15/7, 2010 at 14:49 Comment(4)
+1, I completely agree with the "not perfectly immutable" part. You'll hate it if your ten year old application is based on a political model from the past and you don't get support for it anymore...Infinitude
Disagree that there's not a big performance difference; enums in C# are just ints, so it's much quicker to cast between them and ints. Your points are good, but I'd be hard-pressed to recommend sacrificing performance for possibly-unnecessary flexibility.Flocky
@TMN: Even if enums would be quicker, we're talking about such high speeds that it wouldn't be noticeable in the vast majority of applications. I would be hard-pressed to recommend sacrificing flexibility for a minuscule improvement in performance. However, as it turns out, a Dictionary lookup will actually be a lot faster for the sort of thing he's talking about doing. See my updated answer.Wernsman
PS--Make sure the enum values are all explicitly declared. Otherwise it's too easy for someone to accidentally insert or reorder a value, and mess up your database relationship.Wernsman
D
2

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 !

Dominicadominical answered 15/7, 2010 at 15:15 Comment(2)
Because the integer values are mapped in the database, it would be good to make it possible to specify explicit int values, so they don't change if they get reordered.Wernsman
Well, I do think it wouldn't be too hard to change the code to allow that.Dominicadominical
M
0

Alternatively you can use constants

Mathur answered 15/7, 2010 at 17:37 Comment(1)
How would using constants be advantageous here?Wernsman
F
0

If the question was "is casting enum faster than accessing a dictionary item?" then the other answers addressing the various aspects of the performance would make sense.

But here the question seems to be "is casting enum when I need to store their value to a database table going to negatively affect the application performance?".

If that is the case, I don't need to run any test to say that storing data in a database table is always going to be orders of magnitude slower than casting an enum or executing its ToString().

In this case I would say the important thing is readability and maintainability of the code. In simple cases enums will do the job cleanly, but I agree with other answers that dictionaries are more flexible in the long term.

Folkrock answered 26/4, 2021 at 15:57 Comment(0)
W
0

Use [Flags] attribute. If you want to get the fields of any two values in common, it might be a better way to do it than assigning a new value to it. For example:

[Flags]
public enum MyEnum

{
  statusA = 1,
  statusB = 2,
  both = status1 | status2 //This value = 3 ( 1+2 ) 
}
Wingspread answered 25/7, 2023 at 7:50 Comment(1)
Per Microsoft's Design Guidelines, "Use powers of two for the flag enum values so they can be freely combined using the bitwise OR operation" (source). Based on this, you do not want to do a bitwise or here. I believe you can do a left shift here, though? (status1 << status2)Brogle
M
-1

Enums will greatly outperform almost anything, especially dictionary's. Enums only use single byte. But why would you be casting? Seems like you should be using the enums everywhere.

Maidinwaiting answered 15/7, 2010 at 14:53 Comment(2)
Enums will usually use 4 bytes - their underlying type defaults to int unless you explicitly declare otherwise.Transudation
The "greatly outperform" part depends largely on what you're doing with the enum once you get it. If you use it in a switch statement, enums will be faster. If you call ToString on it, a pre-populated Dictionary will be faster. See my answer.Wernsman
C
-6

Avoid enum as you can: enums should be replaced by singletons deriving from a base class or implementing an interface.

The practice of using enum comes from an old style programming in C.

You start to use an enum for the US States, then you will need the number of inhabitants, the capitol..., and you will need a lot of big switches to get all of this infos.

Constriction answered 15/7, 2010 at 15:4 Comment(2)
Enums in C# aren't objects. They don't really derive from a base class or implement an interface. They are just type-safe constants. They can be useful when used as such, but they are dangerous if misunderstood.Wernsman
I know that enums aren't object. This is the reason i say they are useless. Anyway i made clear my answer.Constriction

© 2022 - 2024 — McMap. All rights reserved.