views:

4302

answers:

8

What is the most efficient way to create a constant (never changes at runtime) mapping of strings to ints?

I've tried using a const Dictionary, but that didn't work out.

I could implement a immutable wrapper with appropriate semantics, but that still doesn't seem totally right.


For those who have asked, I'm implementing IDataErrorInfo in a generated class and am looking for a way to make the columnName lookup into my array of descriptors.

I wasn't aware (typo when testing! d'oh!) that switch accepts strings, so that's what I'm gonna use. Thanks!

+1  A: 

There does not seem to be any standard immutable interface for dictionaries, so creating a wrapper seems like the only reasonable option, unfortunately.

Edit: Marc Gravell found the ILookup that I missed - that will allow you to at least avoid creating a new wrapper, although you still need to transform the Dictionary with .ToLookup().

If this is a need constrained to a specific scenario, you might be better off with a more business-logic-oriented interface:

interface IActiveUserCountProvider
{
    int GetMaxForServer(string serverName);
}
Sander
+8  A: 

There are precious few immutable collections in the current framework. I can think of one relatively pain-free option in .NET 3.5:

Use Enumerable.ToLookup() - the Lookup<,> class is immutable (but multi-valued on the rhs); you can do this from a Dictionary<,> quite easily:

    Dictionary<string, int> ids = new Dictionary<string, int> {
      {"abc",1}, {"def",2}, {"ghi",3}
    };
    ILookup<string, int> lookup = ids.ToLookup(x => x.Key, x => x.Value);
    int i = lookup["def"].Single();
Marc Gravell
A: 

What's about public static readonly?

Tamir
This would make the Dictionary **reference** read-only, not the contents themselves.
Sander
Actually, I was failed to find real scenario for mapping strings to ints, except messagebag. However, if it's about Pyton-based programming, I'll vote for using enum constant instead of all those :)
Tamir
+3  A: 
enum Constants
{
    Abc = 1,
    Def = 2,
    Ghi = 3
}

...

int i = (int)Enum.Parse(typeof(Constants), "Def");
Richard Poole
Interesting idea! I'm wondering how good the Parse() call performs. I fear only a profiler can answer that one.
David Schmitt
+16  A: 

Creating a truly compile-time generated constant dictionary in C# is not really a straightforward task. Actually, none of the answers here really achieve that.

There is one solution though which meets your requirements, although not necessarily a nice one; remember that according to the C# specification, switch-case tables are compiled to constant hash jump tables. That is, they are constant dictionaries, not a series of if-else statements. So consider a switch-case statement like this:

switch (myString)
{
   case "cat": return 0;
   case "dog": return 1;
   case "elephant": return 3;
}

This is exactly what you want. And yes, I know, it's ugly.

DrJokepu
Hey, I just learned something.
mackenir
It's generated code anyways, has good performance characteristics, can be compile-time computed, and has other nice properties for my use case too. I'll be doing it like this!
David Schmitt
just be careful that returning non-value types, even like this, will break your classes immutability.
Tom Anderson
+2  A: 

This is the closest thing you can get to a "CONST Dictionary":

public static int GetValueByName(string name)
{
    switch (name)
    {
        case "bob": return 1;
        case "billy": return 2;
        default: return -1;
    }
}

The compiler will be smart enough to build the code as clean as possible.

Timothy Khouri
A: 

what if we want to populate the dictionary from database?

awaisj
Good question! Populating the dict at run-time doesn't allow the dict to be compile-time optimized like the switch() statement. I'd go for a ToDictionary() or ToLookup() Linq call there. See e.g. http://msdn.microsoft.com/en-us/library/system.linq.enumerable.tolookup.aspx
David Schmitt
A: 

Why not:

public class MyClass
{
    private Dictionary<string, int> _myCollection = new Dictionary<string, int>() { { "A", 1 }, { "B", 2 }, { "C", 3 } };

    public IEnumerable<KeyValuePair<string,int>> MyCollection
    {
        get { return _myCollection.AsEnumerable<KeyValuePair<string, int>>(); }
    }
}
Superstringcheese
Because this is quite expensive compared to the available alternatives under the specified conditions.
David Schmitt