我已经尝试使用const Dictionary,但是那样做并没有奏效。
我可以实现一个immutable wrapper以适当的语义,但这似乎还不完全正确。
对于那些询问过的人,我正在一个生成的类中实现IDataErrorInfo,并且正在寻找一种方法将columnName查找到我的描述符数组中。
当测试时我没有意识到(打了个错字!d'oh!),switch可以接受字符串,所以我会使用它。谢谢!
在C#中创建一个真正的编译时生成的常量字典并不是一项简单的任务。实际上,这里提供的答案都没有真正实现。
不过有一种解决方案可以满足您的需求,但并不一定很好;请记住,根据C#规范,switch-case表被编译为常量哈希跳转表。也就是说,它们是常量字典,而不是一系列if-else语句。因此,请考虑像这样的switch-case语句:
switch (myString)
{
case "cat": return 0;
case "dog": return 1;
case "elephant": return 3;
}
这正是你所需要的。是的,我知道,它很丑。
当前框架中几乎没有不可变集合。我能想到一个相对无痛的选择是在.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();
enum Constants
{
Abc = 1,
Def = 2,
Ghi = 3
}
...
int i = (int)Enum.Parse(typeof(Constants), "Def");
我不确定为什么没有人提到这一点,但在C#中,对于那些我无法分配const的东西,我使用静态只读属性。
例如:
public static readonly Dictionary<string, string[]> NewDictionary = new Dictionary<string, string[]>()
{
{ "Reference1", Array1 },
{ "Reference2", Array2 },
{ "Reference3", Array3 },
{ "Reference4", Array4 },
{ "Reference5", Array5 }
};
这是你可以获得的最接近于“常量字典”的东西:
public static int GetValueByName(string name)
{
switch (name)
{
case "bob": return 1;
case "billy": return 2;
default: return -1;
}
}
编译器会智能地构建尽可能干净的代码。如果使用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 }
}
)
}
readonly Dictionary<TKey, TValue>
并不等同于一个 ReadOnlyDictionary<TKey, TValue>
。实际上,它们的“只读”方式几乎是相反的。请参见:https://dotnetfiddle.net/v0l4aA。 - Broots Waymb为什么不使用命名空间或类来嵌套您的值?虽然它可能不完美,但非常干净。
public static class ParentClass
{
// here is the "dictionary" class
public static class FooDictionary
{
public const string Key1 = "somevalue";
public const string Foobar = "fubar";
}
}
从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;
}
旧版本和新版本的性能差异可能因不同编译器而异。
由于我要绑定到一个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");
似乎没有标准的不可变字典接口,因此创建包装器似乎是唯一合理的选择,不幸的是。
编辑:Marc Gravell找到了我错过的ILookup - 这将允许您至少避免创建新的包装器,尽管您仍然需要使用.ToLookup()转换字典。
如果这是特定场景下的需求限制,您可能最好使用更面向业务逻辑的接口:
interface IActiveUserCountProvider
{
int GetMaxForServer(string serverName);
}