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

c#, enums, generics, typechecking

Solution

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.

Problem

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 ```

Original source

Related problems