64

I'm trying to convert an Enum array to an int array:

public enum TestEnum
{
Item1,
Item2
}

int[] result = Array.ConvertAll<TestEnum, int>(enumArray, new Converter<TestEnum, int>(Convert.ToInt32));

For some reason Convert.ToInt32 doesn't work when used in Array.ConvertAll, so I had to make some changes:

int[] result = Array.ConvertAll<TestEnum, int>(enumArray, new Converter<TestEnum, int>(ConvertTestEnumToInt));

public static int ConvertTestEnumToInt(TestEnum te)
{
return (int)te;
}

Just out of curiosity, is there any way to have this working without using an extra method?

Regards

2
  • Your example int[] result = Array.ConvertAll<TestEnum, int>(enumArray, new Converter<TestEnum, int>(Convert.ToInt32)); is really the same as int[] result = Array.ConvertAll(enumArray, Convert.ToInt32);. The overload of the ToInt32 method in question takes in an object parameter. The delegate type (a constructed generic type Converter<TestEnum, int>) takes in a TestEnum. Now since TestEnum derives from object, you are surprised everything doesn't work. But this kind of magic (came in C# 2) is only allowed with reference types, and TestEnum is a value type. Commented Jan 17, 2014 at 13:48
  • Now, while Gravell's answer gives the best solution, your approach can be repaired by writing int[] result = Array.ConvertAll(enumArray, x => Convert.ToInt32(x));. In that case the lambda contains an invisible boxing from TestEnum to object. But as I said, Gravell's solution is more elegant and fast. Commented Jan 17, 2014 at 13:51

6 Answers 6

71

Just cast using an anonymous method:

int[] result = Array.ConvertAll<TestEnum, int>(
    enumArray, delegate(TestEnum value) {return (int) value;});

or with C# 3.0, a lambda:

int[] result = Array.ConvertAll(enumArray, value => (int) value);
Sign up to request clarification or add additional context in comments.

6 Comments

+1 Beat me to it with int[] result = Array.ConvertAll(enumArray, value => (int) value);
Very nice! Just used in code and works like a charm, nice to no have to individually convert the long list. Thanks!
How about something as simple as int[] res = (int[])(object)enumArray;? See my answer. Of course, boxing included, but damn, I was so suprised. :)
@quetzalcoatl actually that doesn't include any boxing; odd - I'm pretty sure that didn't used to work, but I can't repro it failing on old frameworks
@MarcGravell what if I wanted to the opposite?
|
64

Luckily for us, C# 3.0 includes a Cast operation:

int[] result = enumArray.Cast<int>().ToArray();

If you stop using arrays and start using IEnumerable<>, you can even get rid of the ToArray() call.

2 Comments

your answer makes no sense. what's enumArray ?
@BaltoStar enumArray is the array of enums that the OP references in his code.
18
enumArray.Select(x => (int) x)).ToArray()

Comments

7

This worked like a charm:

var intArray = enumArray.Select(e => (int)e).ToArray();

as did this:

var intList = enumArray.Select(e => (int)e).ToList();

Comments

4

FYI: tested on .Net4ClientProfile and VS2010

Actually, you don't even need to use LINQ. You can just cast it in a normal way, provided that you drop the type down to object.

Having:

  enum One { one0, one1, one2, one3 };
  enum Two { two0, two1, two2, two3 };

  One[] vals = new One[] { One.one0, One.one3 };

we can play:

//Two[] aa = (Two[])vals; // impossible
  Two[] aa = (Two[])(object)vals; // possible!

  Two bb = aa[1]; // == Two.two3

At first, I was really suprised that the second line doesn't throw InvalidCast. But it does not.

Looking at the types explains things a little:

  bool check2 = bb.GetType().FullName == "Two";   // well, you'd guess that
  bool check3 = aa.GetType().FullName == "One[]"; // what?!

Seriously! The aa array is not Two[]. The array type has been "lost by variable" when it was cast to object, but both vals and (object)vals of course still refer to the same object. Then, after the following cast, it's still the aa object, the original array of One[], just hidden behind a new variable type.

The bb object/variable has type of Two because the array's item read as an item of a Two[] array (along with the variable's types). The real array One[] is concealed by the Two[] type, so indexing the Two[] must result in value of Two type.

Furthermore, since the actual type is hidden and since Enum types seem to be treated lightly, let's check another thing:

  var numbers = (int[])(object)vals;
  var cc = numbers[0] + 10; // == 10, from one0's value
  var dd = numbers[1] + 10; // == 13, from one3's value

and similarly:

  bool check4 = numbers.GetType().FullName == "One[]"; // not System.Int32[]

So, as you might already guess, the other way around is possible too:

  var numbers2 = new int[]{ 0, 2, 99 };
  var enums = (One[])(object)numbers2;
  One ee = enums[0]; // == One.one0
  One ff = enums[1]; // == One.one2
  One gg = enums[2]; // == ((One)99)

and int[] also remembers its real type, even if casted to One[]:

  bool check5 = numbers2.GetType().FullName == "System.Int32[]";

Even further, you cannot trust the as and is operators when the array is passed as object:

  bool really_true1 = vals is One[];
  bool really_true2 = vals is Two[];
  bool really_true3 = vals is System.LoaderOptimization[];

This one was a 'gotha!' for me recently.

It actually reuses the original array object (instead of duplicating it like with LINQ) and it exposes it as different type - be it Two[] or int[]. It's seems to be a "true cast", no boxing. Much different than LINQ-driven copying&conversion.

8 Comments

btw: as always, JS already wrote about that (stackoverflow.com/a/15168879/717732) and his words somewhat suggest that there is no extra boxing "as the CLR knows there's no difference".
this isn't boxing; you can't "box" an array, since an array is always a reference type; very interesting
Thanks for the note, I don't know why I didn't thought in these terms, and looking at it like that it's obvious. I've corrected the bits in the text.
@MarcGravell is right, it is not boxing. In the example (int[])(object)(new One[5]) we are "mis-using" the fact that the runtime considers types int[] and One[] assignable from each other, in some sense. C# doesn't agree with the runtime's concept of assignability in this case. In the example (object[])(object)(new System.Enum[5]) we are simply using the array covariance which works only with classes (that is reference types). Remember that System.Enum is a class, a reference type, the base class of your One` type.
You are absolutely right. Another example, related, is uint[] myUnsignedArray = (uint[])(Array)(new[] { -2, -4, -6, });. The runtime allows to treat a reference to a System.Int32[] object as a System.UInt32[] reference. Of course myUnsignedArray.GetType() will get the real type. But this is a violation of the C# spec according to which no reference conversion between int[] and uint[] shall exist. Which is why we have to put in the intermediate cast to object or Array or something, to satisfy the C# compiler.
|
2

int[] b = Array.ConvertAll((int[])Enum.GetValues(typeof(TestEnum)), Convert.ToInt32);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.