在C#中创建常量字典


128

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

我试过使用const Dictionary,但是没有解决。

我可以用适当的语义实现一个不变的包装器,但这似乎并不完全正确。


对于那些提出要求的人,我正在生成的类中实现IDataErrorInfo,并正在寻找一种方法来使columnName查找进入我的描述符数组。

我不知道(测试时输入错误!d!哦!)该开关接受字符串,所以这就是我要使用的。谢谢!


1
有一个解决方案在这里: stackoverflow.com/questions/10066708/...

Answers:


180

用C#创建一个真正的编译时生成的常量字典并不是一件容易的事。实际上,这里没有任何答案能真正做到这一点。

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

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

这正是您想要的。是的,我知道,这很丑陋。


9
无论如何,它都是生成的代码,具有良好的性能特征,可以在编译时进行计算,并且对于我的用例也具有其他不错的属性。我会这样做!
David Schmitt,

4
请注意,即使返回这样的非值类型,也会破坏类的不变性。
汤姆·安德森

35
switch-case很棒,直到您需要“遍历”值为止。
亚历克斯(Alex)

6
它缺少字典具有的许多出色功能。
邢子南2014年

1
@TomAnderson:如果返回的项目是值类型(可变或非可变),或者它们是不可变的类对象,则该构造将表现为不可变。
超级猫

37

当前框架中很少有不可变的集合。我可以想到.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();

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

...

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

2
有趣的主意!我想知道Parse()调用的性能如何。我担心只有分析器才能回答那个问题。
David Schmitt,

10

这是您最接近“ CONST词典”的内容:

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

编译器将足够聪明,以使代码尽可能清晰。


8

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

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

private static readonly Dictionary<string, string> _yourDictionaryName = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase) { { "One",1 }, { "Two",2 }, { "Three",3 }, };这就是我的方法。
Sanket Sonavane

@SanketSonavane a readonly Dictionary<TKey, TValue>不等同于ReadOnlyDictionary<TKey, TValue>。实际上,它们以“只读”方式彼此几乎是相反的。请参阅:dotnetfiddle.net/v0l4aA
Broots Waymb

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等。


因为这没有实现IDataErrorInfo接口。
David Schmitt '18

1

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

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

如果这仅限于特定情况,则最好使用更加面向业务逻辑的界面:

interface IActiveUserCountProvider
{
    int GetMaxForServer(string serverName);
}

1

我不确定为什么没有人提到这个,但是在C#中由于无法分配const的原因,我使用了静态只读属性。

例:

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

因为字典的内容仍然可变,并且在运行时分配。
戴维·施密特

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;

为了从显示名称的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");

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

3
因为与指定条件下的可用替代品相比,这是非常昂贵的。
David Schmitt,2010年

同样是因为它甚至无法编译
-AustinWBryan

@AustinWBryan是的,它可以编译
derHugo
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.