Creating a constant Dictionary in C#

Publish date: 2024-05-21

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

11 Answers

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.

6

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(); 
enum Constants { Abc = 1, Def = 2, Ghi = 3 } ... int i = (int)Enum.Parse(typeof(Constants), "Def"); 
1

I am not sure why no one mentioned this but in C# for things that I cannot assign const, I use static read-only properties.

Example:

public static readonly Dictionary<string, string[]> NewDictionary = new Dictionary<string, string[]>() { { "Reference1", Array1 }, { "Reference2", Array2 }, { "Reference3", Array3 }, { "Reference4", Array4 }, { "Reference5", Array5 } }; 
1

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.

If using 4.5+ Framework I would use ReadOnlyDictionary (also ReadOnly Collection for lists) to do readonly mappings/constants. It's implemented in the following way.

static class SomeClass { static readonly ReadOnlyDictionary<string,int> SOME_MAPPING = new ReadOnlyDictionary<string,int>( new Dictionary<string,int>() { { "One", 1 }, { "Two", 2 } } ) } 
2

Why not use namespaces or classes to nest your values? It may be imperfect, but it is very clean.

public static class ParentClass { // here is the "dictionary" class public static class FooDictionary { public const string Key1 = "somevalue"; public const string Foobar = "fubar"; } } 

Now you can access .ParentClass.FooDictionary.Key1, etc.

1

As of C# 8, the new switch expression is the cleanest way to achieve the result:

int value = inputString switch { "one" => 1, "two" => 2, _ => -1 }; 

or as a function

int GetValue(string inputString) => inputString switch { "one" => 1, "two" => 2, _ => -1 }; 

It is more concise that the "old" one:

int value = -1; switch (inputString){ case "one": value=1; break; case "two": value=2; break; } 

The difference in performance of the old and new one can vary across different compilers

2

Just another idea since I am binding to a winforms combobox:

public enum DateRange { [Display(Name = "None")] None = 0, [Display(Name = "Today")] Today = 1, [Display(Name = "Tomorrow")] Tomorrow = 2, [Display(Name = "Yesterday")] Yesterday = 3, [Display(Name = "Last 7 Days")] LastSeven = 4, [Display(Name = "Custom")] Custom = 99 }; int something = (int)DateRange.None; 

To get the int value from the display name from:

public static class EnumHelper<T> { public static T GetValueFromName(string name) { var type = typeof(T); if (!type.IsEnum) throw new InvalidOperationException(); foreach (var field in type.GetFields()) { var attribute = Attribute.GetCustomAttribute(field, typeof(DisplayAttribute)) as DisplayAttribute; if (attribute != null) { if (attribute.Name == name) { return (T)field.GetValue(null); } } else { if (field.Name == name) return (T)field.GetValue(null); } } throw new ArgumentOutOfRangeException("name"); } } 

usage:

var z = (int)EnumHelper<DateRange>.GetValueFromName("Last 7 Days"); 

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); } 

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>>(); } } }
3

ncG1vNJzZmirpJawrLvVnqmfpJ%2Bse6S7zGiorp2jqbawutJoaW9wYG2BcK%2FRnpitoZ6ceqJ5wqilrKyRo8FusMicq6Knnpa%2FunnIp2ScZaOdrrO8