我有很多固定大小的数字集合,其中每个条目都可以通过常量访问。自然而然地,这似乎指向数组和枚举:
enum StatType {
Foo = 0,
Bar
// ...
}
float[] stats = new float[...];
stats[StatType.Foo] = 1.23f;
当然,这样做的问题是你不能使用枚举来索引数组,除非进行类型转换(尽管编译后的IL使用普通整数)。因此,你必须在许多地方重复编写以下代码:
stats[(int)StatType.foo] = 1.23f;
我曾尝试找到不需要强制转换的简单语法,但是还没有找到完美的解决方案。使用字典似乎是行不通的,因为我发现它比数组慢了约320倍。我还尝试编写一个通用类来将枚举作为索引的数组:
public sealed class EnumArray<T>
{
private T[] array;
public EnumArray(int size)
{
array = new T[size];
}
// slow!
public T this[Enum idx]
{
get { return array[(int)(object)idx]; }
set { array[(int)(object)idx] = value; }
}
}
甚至可以使用第二个泛型参数来指定枚举类型。这很接近我想要的,但问题是您不能将未指定的枚举(无论是来自泛型参数还是装箱类型Enum)直接转换为int。相反,您必须先使用对象强制转换进行装箱,然后再进行强制转换。这样做虽然可行,但速度较慢。我发现索引器生成的IL大致如下:
.method public hidebysig specialname instance !T get_Item(!E idx) cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: ldfld !0[] EnumArray`2<!T, !E>::array
L_0006: ldarg.1
L_0007: box !E
L_000c: unbox.any int32
L_0011: ldelem.any !T
L_0016: ret
}
正如你所看到的,这里有一些不必要的盒子(box)和取消盒子(unbox)指令。如果你从二进制代码中去掉它们,那么代码就可以很好地运行,并且只比纯数组访问略慢一点。
有没有什么简单的方法来解决这个问题?或者说甚至有更好的方法吗? 我认为也可以使用自定义属性标记此类索引器方法,并在编译后删除这两个指令。哪个库比较适用?也许是Mono.Cecil?
当然,总是有可能放弃枚举并像这样使用常量:
static class StatType {
public const int Foo = 0;
public const int Bar = 1;
public const int End = 2;
}
这可能是最快的方法,因为您可以直接访问数组。