在C#enum中使用sentinel值(编译时枚举的大小)?

时间:2012-02-24 17:25:47

标签: c# arrays enums new-operator

我在处理嵌入式API(主要是通信协议)时经常在C中使用的快捷方式允许我编辑枚举数组并在此之后正确设置其他所有内容:

typedef enum {
   errortype1,
   errortype2,
   ...
   errortypeN,
   ERROR_TYPE_MAX
} ErrorTypeList;

int errorcounts[ERROR_TYPE_MAX]; // Create array to hold a counter for each error type

只要我在ERROR_TYPE_MAX之前添加新的错误类型,那么我可以在其他地方使用该值来为我提供枚举的大小。

但是,在C#中,这不能按原样运行:

enum ErrorTypeList {
   errortype1,
   errortype2,
   ...
   errortypeN,
   ERROR_TYPE_MAX
};

int errorcounts[ErrorTypeList.ERROR_TYPE_MAX]; // Create array to hold a counter for each error type

此用法会显示错误Array size cannot be specified in a variable declaration,建议使用new

是在运行时定义数组(通过new)我唯一的选择,还是有一种方法可以在没有新的情况下完成此操作,因为编译后枚举的大小不会改变?

这种类型的枚举大小模式是否可以替代c#?

5 个答案:

答案 0 :(得分:6)

如果你想要枚举的 size ,你可以这样做:

enum Foo { A , B , C , ... , }
...
int[] someArray = new int[ sizeof(Foo) ] ;

但是这不会给你你想要的东西,因为sizeof以八位字节的形式返回对象的大小(在这种情况下为4,因为枚举,默认包装Int32)。

要在枚举中获取 ,请执行以下操作:

enum Foo { A , B , C , ... , }
...
int[] someArray = new int[ Enum.GetValues(typeof(Foo)).Length ] ;

如果你打算使用枚举的值来索引数组,你应该注意到这使得[无根据]假设enum的值从0开始并递增1:随后对枚举的更改违反这一假设的定义会破坏事物。

此外,如果枚举应用了[Flags]属性,那么枚举的域可能[远]大于定义的离散值的数量。

你最好使用字典,因此:

[Flags]
enum Foo { Unknown = 0 , A = 1 , B = 2 , C = 4 , D = 8 , E = 16 , ... , }
...
Dictionary<Foo,SomeType> myDictionary = new Dictionary<Foo,SomeType>() ;

现在你的代码更加灵活:你不关心如何安排枚举的内部。

您甚至可以使用预期的键集预先填充字典(如果传递的值不是预期的值,则抛出异常)。

编辑注意:

你也可以这样做:

void DoSomething()
{
  int[] someArray = CreateArrayBasedOnFoo<int>() ;
  ...
  return ;
}

public T[] CreateArrayBasedOnFoo<T>()
{
  Foo[] values = (Foo[]) Enum.GetValues(typeof(Foo)) ;
  int   lo     = (int) values.Min() ;
  int   hi     = (int) values.Max() ;
  int  domain  = ( hi - lo ) + 1 ;
  T[] instance = (T[]) Array.CreateInstance( typeof(T), new int[]{domain} , new int[]{lo} ) ;

  return instance ;
}

CreateArrayBasedOnFoo<T>()方法将返回一个大小适合枚举的数组,其下限是枚举的最小值。例如,如果枚举有3个离散值:3,5和7,则返回的数组长度为5个元素,下限为3。

答案 1 :(得分:5)

您必须使用new

变量/字段声明不指定数组的大小,只有在构造数组实例时才会完成。

最多可以指定数组的维数,但不能指定每个维度的大小。

答案 2 :(得分:2)

您只能在unsafe struct中定义固定长度的数组。否则C#仅支持使用new在运行时创建数组。

由于数组是引用类型并存储在堆上,因此不会产生任何性能差异。

答案 3 :(得分:2)

要回答问题的第二部分,您可以使用enum类获取Enum中的条目数,如下所示:

enum ErrorTypeList {
   errortype1,
   errortype2,
   ...
   errortypeN // ERROR_TYPE_MAX is not necessary
};


int errorCounts[] = new int[Enum.GetNames(typeof(ErrorTypeList)).Length];

请注意,数组中的计数器技巧仅在enum值连续时才有效。这比Dictionary<ErrorTypeList,int>更脆弱,即使您决定将枚举常量指定为非连续值或负值,它也会起作用。当然,您需要在开始时将计数初始化为零,以避免在运行时出现“元素不存在”异常。

答案 4 :(得分:0)

由于枚举条目初始值设定项,我认为使用枚举条目数对C#中的枚举值执行边界检查是不安全的做法。

有足够的验证方法,如Enum.IsDefined()和Enum.ToObject(),可以更安全地验证数值并将其转换为特定类型的枚举条目。

请参阅Enum类的MSDN参考:

http://msdn.microsoft.com/en-us/library/system.enum.aspx

编辑以解决澄清的问题陈述......

我认为字典映射枚举值和整数计数器可以更好地满足您的需求。如果某人在一个或多个枚举条目上粘贴初始值设定项,它仍然有效。

enum MyEnum {a=32, b, c, d=48, e, f}

Dictionary<MyEnum, int>ErrorCounts = new Dictionary<MyEnum, int>();

如果你真的想要预先初始化它:

foreach(MyEnum me in Enum.GetValues(typeof(MyEnum)))
{
    ErrorCounts[me] = 0;
}

否则,我可能只会在出现错误情况时添加条目

private void IncrementErrorCount(MyEnum val)
{
    if(ErrorCounts[val] == null)
        ErrorCounts[val]=1
    else
        ErrorCounts[val]++;
}