在C#中创建一个常量字典

时间:2008-11-06 09:38:23

标签: c# .net collections dictionary constants

创建常量(从不在运行时更改)映射stringint s的最有效方法是什么?

我尝试使用const Dictionary,但这没有用。

我可以使用适当的语义实现immutable wrapper,但这似乎仍然不完全正确。


对于那些曾经问过的人,我正在生成的类中实现IDataErrorInfo,并且正在寻找一种方法来将columnName查找到我的描述符数组中。

我不知道(测试时的错字!d!哦!)开关接受字符串,这就是我要用的东西。谢谢!

10 个答案:

答案 0 :(得分:156)

在C#中创建一个真正的编译时生成的常量字典并不是一个简单的任务。实际上,这里的答案都没有真正实现。

有一种解决方案可以满足您的要求,但不一定很好;请记住,根据C#规范,switch-case表被编译为常量哈希跳转表。也就是说,它们是不变的字典,而不是一系列if-else语句。所以考虑一下像这样的switch-case语句:

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

这正是你想要的。是的,我知道,这很难看。

答案 1 :(得分:35)

目前的框架中有很少的不可变集合。我可以想到.NET 3.5中一个相对无痛的选项:

使用Enumerable.ToLookup() - Lookup<,>类是不可变的(但在rhs上是多值的);你可以很容易地从Dictionary<,>做到这一点:

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

答案 2 :(得分:14)

enum Constants
{
    Abc = 1,
    Def = 2,
    Ghi = 3
}

...

int i = (int)Enum.Parse(typeof(Constants), "Def");

答案 3 :(得分:10)

这是你最接近“CONST词典”的东西:

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

编译器足够智能,可以尽可能地构建代码。

答案 4 :(得分:3)

如果使用4.5+ Framework,我将使用ReadOnlyDictionary(也用于列表的ReadOnly集合)进行只读映射/常量。它是通过以下方式实现的。

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

答案 5 :(得分:2)

为什么不使用命名空间或类来嵌套值?它可能不完美,但非常干净。

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

现在您可以访问.ParentClass.FooDictionary.Key1等

答案 6 :(得分:1)

字典似乎没有任何标准的不可变接口,所以不幸的是,创建一个包装似乎是唯一合理的选择。

编辑:Marc Gravell找到了我错过的ILookup - 这将允许您至少避免创建新的包装器,尽管您仍需要使用.ToLookup()转换Dictionary。

如果需要限制特定情况,那么使用面向业务逻辑的界面可能会更好:

interface IActiveUserCountProvider
{
    int GetMaxForServer(string serverName);
}

答案 7 :(得分:0)

我不确定为什么没有人提到这一点,但是对于无法分配const的C#,我使用静态只读属性。

示例:

data Some = A | B | C deriving (Eq, Show, Ord)
canBeOrderedList [A,A,B] [C,A,A]
=> [False,False,True]

答案 8 :(得分:0)

由于我绑定到Winforms组合框,这是另一个想法:

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;

要从显示名称from中获取int值:

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

用法:

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

答案 9 :(得分:-2)

为什么不:

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