It appears that, in .NET, “array of enum” is not a strongly-typed concept. MyEnum[] is considered to implement not just IEnumerable<MyEnum>, but also IEnumerable<YourEnum>. (I didn’t believe it at first either.)
// Returns true:
typeof(IEnumerable<DayOfWeek>).IsAssignableFrom(typeof(AttributeTargets[]))
// Outputs "3":
var listOfLists = new List<object> {
new[] { AttributeTargets.All },
new[] { ConsoleColor.Blue },
new[] { PlatformID.Xbox, PlatformID.MacOSX }
};
Console.WriteLine(listOfLists.OfType<IEnumerable<DayOfWeek>>().Count());
So when I go looking through a list for everything that implements IEnumerable<T>, I’m getting the T[]s and the List<T>s and the iterator-generated IEnumerable<T>s, but I’m also getting the SomethingElse[]s that I do not want.
What’s the easiest way to find out whether a given Type (as with IsAssignableFrom above) or a given instance (as with OfType<T> above) really and truly implements, say, IEnumerable<DayOfWeek>?
I gave Mr. Skeet the green checkmark, but once I got his code into Visual Studio, ReSharper suggested a more concise version. Use whichever version you prefer.
public static IEnumerable<IEnumerable<T>> OfSequenceType<T>
(this IEnumerable source) where T : struct
{
return from sequence in source.OfType<IEnumerable<T>>()
let type = sequence.GetType()
where !type.IsArray || type.GetElementType() == typeof (T)
select sequence;
}
I believe this is basically due to section 8.7 of ECMA 335.
Basically we’re looking at the assignable-to relationship between the two array-of-enum types. As far as I can tell, 8.7.2 is applicable:
So we look to 8.7.1 and find bullet 5:
So now we’re interested in whether the two enum types have an array-element-compatible-with relationship… That then leads to:
Now the underlying type of an enum is defined by this:
So in the case of, say:
the underlying types of both are
int.Now we can go back to our array-element-compatible-with definition, and see that both V and W are
int. A type is compatible-with itself due to the first bullet of 8.7.1:Therefore, the arrays are compatible.
They’re also compatible with an array of the underlying type itself:
Note that
xhas to be declared asobjecthere to persuade the C# compiler that this might be legal – otherwise it follows the rules of the C# language, which aren’t quite so forgiving…Now as for your second question:
You could just special case arrays, as they behave a little oddly. Frankly that’s probably the easiest approach. I tried using
Type.GetInterfaceMapbut that gives a problem too:(Yes, the typo at the end is really in the error message. Can’t be bothered to raise a Connect issue for that though…)
I strongly suspect that special-casing is the way forward… for example, assuming that you know you’re dealing with a value type (covariance of reference type arrays is a separate matter…)