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

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

我试过用 Const 字典,但没用。

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


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

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

148939 次浏览

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

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

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

interface IActiveUserCountProvider
{
int GetMaxForServer(string serverName);
}

在当前的框架中,不可变的集合非常少。我可以想到一个相对无痛的选择。净值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();
enum Constants
{
Abc = 1,
Def = 2,
Ghi = 3
}


...


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

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

尽管有一个解决方案满足您的需求,但不一定是一个很好的解决方案; 请记住,根据 C # 规范,switch-case 表被编译为常量 hash 跳转表。也就是说,它们是常量字典,而不是一系列 if-else 语句。因此,考虑一个这样的开关情况陈述:

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

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

这是你能得到的最接近于“ CONST 字典”的东西:

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

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

为什么不:

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

为什么不使用名称空间或类来嵌套您的值呢。

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

如果使用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 }
}
)
}

我不知道为什么没有人提到这一点,但在 C # 中,对于我不能赋值 const 的内容,我使用静态只读属性。

例如:

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

只是另一个想法,因为我绑定到一个 winform 组合框:

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

从 C # 8开始,新的开关表达式新的开关表达式是实现这一结果的最干净的方式:

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

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