C# C语言中使用枚举索引数组#

C# C语言中使用枚举索引数组#,c#,arrays,enums,cil,C#,Arrays,Enums,Cil,我有很多固定大小的数字集合,每个条目都可以通过常量访问。当然,这似乎指向数组和枚举: enum StatType { Foo = 0, Bar // ... } float[] stats = new float[...]; stats[StatType.Foo] = 1.23f; 当然,这样做的问题是,在没有强制转换的情况下,不能使用枚举对数组进行索引(尽管编译后的IL使用的是纯整数)。所以你必须把这些写得到处都是: stats[(int)StatType.foo]

我有很多固定大小的数字集合,每个条目都可以通过常量访问。当然,这似乎指向数组和枚举:

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

这可能是最快的方法,因为您可以直接访问数组。

不幸的是,我不认为有任何方法可以向枚举添加隐式转换运算符。因此,您要么使用难看的类型转换,要么只使用带有常量的静态类

这里有一个StackOverflow问题,详细讨论了隐式转换运算符:

我想,通过编译一个委托来为您执行转换,这样就不需要装箱和拆箱,您可能可以使转换速度更快一些。如果您使用的是.NET3.5,那么表达式树可能是实现这一点的最简单方法。(您可以在EnumArray示例中使用它。)

就个人而言,我很想使用您的
const int
解决方案。它不像.NET默认情况下提供枚举值验证-即调用方总是可以将
int.MaxValue
强制转换为枚举类型,并且您会得到一个ArrayIndexException(或其他)。因此,考虑到您已经得到的保护/类型安全性相对不足,定值答案很有吸引力


希望Marc Gravell能在一分钟内完成编译转换委托的想法,尽管……

枚举应该是类型安全的。如果您将它们用作数组的索引,那么您正在修复枚举的类型和值,因此声明int常量的静态类对您没有好处。

如果您的枚举数组不是泛型的,而是显式地使用StatType索引器,那么您就可以了。如果这不可取,那么我自己可能会使用const方法。但是,传入Func的快速测试与直接访问没有明显的区别

 public class EnumArray<T, E> where E:struct {
    private T[] _array;
    private Func<E, int> _convert;

    public EnumArray(int size, Func<E, int> convert) {
        this._array = new T[size];
        this._convert = convert;
    }

    public T this[E index] {
        get { return this._array[this._convert(index)]; }
        set { this._array[this._convert(index)] = value; }
    }
 }
公共类枚举数组,其中E:struct{
专用T[]_数组;
私有函数转换;
公共枚举数组(整数大小,Func转换){
这个。_数组=新的T[大小];
这个。_convert=convert;
}
公共T此[E索引]{
获取{返回此。_数组[此。_转换(索引)];}
设置{this.\u数组[this.\u convert(index)]=value;}
}
}

如果有很多固定大小的集合,那么将属性封装在对象中可能比封装在浮点[]中更容易:

public class Stats
{
    public float Foo = 1.23F;
    public float Bar = 3.14159F;
}
传递对象将为您提供所需的类型安全性、简洁的代码和恒定的时间访问


如果你真的需要使用数组,那么添加一个ToArray()方法将对象的属性映射到一个float[]就很容易了。

我对C#不是100%熟悉,但我以前见过隐式操作符用于将一种类型映射到另一种类型。是否可以为枚举类型创建一个隐式运算符,以便将其用作int

struct PseudoEnum 
{ 
    public const int INPT = 0; 
    public const int CTXT = 1; 
    public const int OUTP = 2; 
}; 

// ... 

String[] arr = new String[3]; 

arr[PseudoEnum.CTXT] = "can"; 
arr[PseudoEnum.INPT] = "use"; 
arr[PseudoEnum.CTXT] = "as"; 
arr[PseudoEnum.CTXT] = "array"; 
arr[PseudoEnum.OUTP] = "index"; 
(我也把这个答案贴在了)


[编辑:噢,我刚刚注意到Steven Behnke在本页的其他地方提到了这种方法。对不起,但至少这显示了一个这样做的示例…]

虽然这可能对简单的事情有好处,但它使循环部分值变得困难。由于数组和枚举之间存在1:N关系,因此更新它们也很麻烦,例如,某些枚举用于索引多个数组。您只能在源类或目标类中创建这些转换器。因此,要做到这一点,您必须更改
Int32
类或枚举的类,但您不能这样做。这似乎是一个很好的转换泛型参数的方法。虽然生成的IL包含对委托的callvirt,并且在编译或执行期间没有进行优化。我想如果您真的可以在编译时对(某些)枚举强制执行类型安全性,那就太好了。也适用于(enumValue+int)等表达式,其中int具有编译时已知的有效范围。
public class Stats
{
    public float Foo = 1.23F;
    public float Bar = 3.14159F;
}
struct PseudoEnum 
{ 
    public const int INPT = 0; 
    public const int CTXT = 1; 
    public const int OUTP = 2; 
}; 

// ... 

String[] arr = new String[3]; 

arr[PseudoEnum.CTXT] = "can"; 
arr[PseudoEnum.INPT] = "use"; 
arr[PseudoEnum.CTXT] = "as"; 
arr[PseudoEnum.CTXT] = "array"; 
arr[PseudoEnum.OUTP] = "index";