Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use IEnumerable.Cast<> and .ToArray() to convert int array to an enum array? [duplicate]

Consider the following enum declaration and int array:

enum Test { None };

int[] data = {0};

To convert this int[] array to an Test[] array we can simply do this:

Test[] result = Array.ConvertAll(data, element => (Test)element);

I initially tried to do this:

Test[] result = data.Cast<Test>().ToArray();

However, that throws an exception at runtime:

System.ArrayTypeMismatchException: Source array type cannot be assigned to destination array type.

Question One

Is there a way to do this using Linq and Cast<> without an error, or must I just use Array.Convert()?

Question Two (added after the first question was answered correctly)

Exactly why doesn't it work?

It seems like it's a case of an implementation detail escaping... Consider:

This causes an error:

var result = data.Cast<Test>().ToList(); // Happens with "ToList()" too.

But this does not:

var result = new List<Test>();

foreach (var item in data.Cast<Test>())
    result.Add(item);

And neither does this:

var result = data.Select(x => x).Cast<Test>().ToList();

The clear implication is that some kind of optimisation is being done in the .ToList() implementation that causes this exception.


Addendum:

This also works:

List<int> data = new List<int>{0};
var result = data.Cast<Test>().ToList();

or

List<int> data = new List<int>{0};
var result = data.Cast<Test>().ToArray();

It's only if the original data is an array that it doesn't work.

like image 853
Matthew Watson Avatar asked Dec 15 '16 16:12

Matthew Watson


3 Answers

You can use .Select(e => (Test)e).

like image 57
Daniel A. White Avatar answered Sep 28 '22 11:09

Daniel A. White


It doesn't work in this case because Cast first performs the following check:

public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source)
{
  IEnumerable<TResult> results = source as IEnumerable<TResult>;
  if (results != null)
    return results;
  // ... the rest of the method
}

It turns out that for int[] this check succeeds (so int[] as IEnumerable<Test> is not null). In result, Cast actually does nothing at all, it returns array as is. Then when you call ToArray, Array.Copy is invoked. It tries to copy from int[] array to Test[] array, and fails because of type mismatch. This exception you see.

like image 35
Evk Avatar answered Sep 28 '22 09:09

Evk


Because the cast actually did not do anything since it thinks that the collection is already the desire type

    IEnumerable<Test> cast = data.Cast<Test>();
    IEnumerable<Test> select = data.Select(e => (Test) e);

    Console.WriteLine(cast == (IEnumerable<int>) data); //True
    Console.WriteLine(select == (IEnumerable<int>) data); //False
    Console.WriteLine(select == cast); //Flase

as you can see the cast IEnumerable is actually just the original array. Thus .ToArray is trying to do something similar to (Test[]) data.ToArray();

like image 41
Steve Avatar answered Sep 28 '22 09:09

Steve