enum[] is IEnumerable<int> returns true in a generic method

337 views Asked by At

This is a followup to this question: Cast<int>.Cast<int?> applied on generic enum collection results in invalid cast exception

enum Gender { Male, Female }

Gender g = Gender.Male;

bool b = g is int; // false, alright no issues
b = new[] { g } is IEnumerable<int>; // false, alright no issues
b = Is<Gender, int>(g); //false, alright no issues
b = Is<Gender[], IEnumerable<int>>(new[] { g }); // true, why on earth !!!

static bool Is<S, T>(S s)
{
    return s is T;
}

Why is that Gender[] is IEnumerable<int> returns true in the generic case? Especially when they are not type compatible?

IEnumerable<int> c = new[] { Gender.Male }; //not compilable

It had tripped me in the question I linked! I think this question is the crux of the issue of the linked question.


For someone interested, this is a corner case with arrays (not really enums). Follow Eric Lippert's blog article in the answer to know more of this edge case. This doesn't happen with List<T> for instance:

b = Is<List<Gender>, IEnumerable<int>>(new List<Gender> { g }); // false, rightly
1

There are 1 answers

3
Michael Liu On BEST ANSWER

I think this is one of those cases where the C# definition of is differs from the CLI's definition of isinst, which evidently treats enums as their underlying base type when checking for array assignment compatibility. (Eric Lippert wrote a blog post that explains why uint[] is treated as an int[] by the CLI but not by C#; I suspect the same explanation applies here.) You don't even need generics to demonstrate:

Gender g = Gender.Male;
Console.WriteLine(new[] { g } is IEnumerable<int>); // False
Console.WriteLine((object)new[] { g } is IEnumerable<int>); // True

The first is expression is optimized to false at compile time because the C# compiler "knows" Gender[] isn't an IEnumerable<int>. The second is expression generates an isinst instruction which is evaluated at run time. Quoting Eric Lippert:

It is unfortunate that C# and the CLI specifications disagree on this minor point but we are willing to live with the inconsistency.