Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using sentinal values in C# enum (size of enum at compile time)?

A shortcut I often use in C when dealing with embedded APIs (communications protocols, primarily) allows me to edit an enum array and have everything else sized correctly after that:

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

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

As long as I add new error types before ERROR_TYPE_MAX then I can use that value everywhere else to give me the size of the enum.

In C#, however, this doesn't work as-is:

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

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

This usage presents the error Array size cannot be specified in a variable declaration which suggests using new.

Is defining the array at runtime (via new) my only option, or is there a way to accomplish this without a new, since the size of the enum isn't going to change after compilation?

Is there an alternative to this type of enum sizing pattern that better fits into c#?

like image 321
Adam Davis Avatar asked Feb 24 '12 17:02

Adam Davis


Video Answer


1 Answers

If you want the size of an enum, you can do this:

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

But that won't give you what you want, since sizeof returns the size of the object in octets (4 in this case, since an enum, by default wraps an Int32).

To get the number of values in the enum, do something like this:

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

If you're planning on using the value of the enum to index into the array, you should note that this makes the [unwarranted] assumption that the enum's values start at 0 and increment by 1: a subsequent change to the enum's definition that violates the assumption will break things.

Further, if the enum has the [Flags] attribute applied to it, then the domain of the enum is potentially [much] larger than the number of discrete values defined.

You would be better off to use a dictionary, thus:

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

Now your code is much more flexible: you don't care about how the internals of the enum are arranged.

You might even pre-populate the dictionary with the expected set of keys (and throw an exception if handed a value other than the expected).

Edited To Note:

You could do something like this as well:

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

The CreateArrayBasedOnFoo<T>() method will hand back an array sized to fit the enum and whose lower bound is the smallest value of the enum. For instance, if you enum has 3 discrete values: 3, 5 and 7, the array handed back will be 5 elements in length and will have a lower bound of 3.

like image 118
Nicholas Carey Avatar answered Oct 27 '22 01:10

Nicholas Carey