148

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

我试过使用const Dictionary,但没有成功。

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


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

我不知道(测试时打错字!d'oh!)那个开关接受字符串,所以这就是我要使用的。谢谢!

4

11 回答 11

197

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

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

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

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

于 2008-11-06T10:26:21.503 回答
40

当前框架中的不可变集合很少。我可以想到 .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();
于 2008-11-06T09:45:26.297 回答
16
enum Constants
{
    Abc = 1,
    Def = 2,
    Ghi = 3
}

...

int i = (int)Enum.Parse(typeof(Constants), "Def");
于 2008-11-06T09:57:52.293 回答
12

这是您可以得到的最接近“CONST Dictionary”的东西:

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

编译器将足够聪明,以尽可能干净地构建代码。

于 2008-11-06T10:39:12.023 回答
8

如果使用 4.5+ 框架,我将使用 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 }
            }
        )
}        
于 2018-10-30T20:20:55.883 回答
7

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

例子:

public static readonly Dictionary<string, string[]> NewDictionary = new Dictionary<string, string[]>()
        {
            { "Reference1", Array1 },
            { "Reference2", Array2 },
            { "Reference3", Array3 },
            { "Reference4", Array4 },
            { "Reference5", Array5 }
        };
于 2019-08-08T14:29:34.693 回答
3

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

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

于 2018-04-10T17:01:05.907 回答
2

只是另一个想法,因为我绑定到一个 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");
于 2019-10-24T18:39:20.603 回答
1

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

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

如果这是一个受限于特定场景的需求,那么使用更面向业务逻辑的接口可能会更好:

interface IActiveUserCountProvider
{
    int GetMaxForServer(string serverName);
}
于 2008-11-06T09:43:14.773 回答
1

从 C# 8 开始,新的 switch 表达式是实现结果的最简洁方式:

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

或作为一个函数

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

“旧”的更简洁:

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

新旧版本的性能差异可能因不同的编译器而异

于 2021-09-14T09:42:42.543 回答
-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>>(); }
    }
}
于 2010-07-27T12:48:25.027 回答