每个枚举数组都实现了IEnumerable <EveryOtherEnum>.我该如何解决这个问题?

Joe*_*ite 8 .net c# enums types

看来,在.NET中,"枚举数组"不是强类型概念.MyEnum[]被认为不仅仅是IEnumerable<MyEnum>,而且IEnumerable<YourEnum>.(我起初也不相信.)

// 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());
Run Code Online (Sandbox Code Playgroud)

因此,当我查看列表中的所有实现时IEnumerable<T>,我将获得T[]s和List<T>s以及迭代器生成的IEnumerable<T>s,但我也得到了SomethingElse[]我不想要的s.

什么是最简单的方法来确定一个给定的Type(如上面的IsAssignableFrom)或给定的实例(OfType<T>如上所述)是否真的真实地实现了IEnumerable<DayOfWeek>


我给了Skeet先生绿色的复选标记,但是一旦我将他的代码放入Visual Studio,ReSharper建议使用更简洁的版本.使用您喜欢的任何版本.

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;
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 10

我认为这基本上是由于ECMA 335的第8.7节.

基本上我们正在研究两个枚举类型之间的可分配关系.据我所知,8.7.2适用:

当且仅当下列之一成立时,位置类型T与位置类型U兼容.

  1. 根据§8.7.1中的定义,T和U不是托管指针类型,T U 兼容.

所以我们看看8.7.1并找到子弹5:

T是从零开始的秩-1阵列V [],并且U是从零开始的秩-1阵列W [],并且V与阵列元素兼容 - 与 W.

所以现在我们对两个枚举类型是否具有与数组元素兼容的关系感兴趣...然后导致:

签名类型T与数组元素兼容 - 具有签名类型U,当且仅当T具有基础类型V且U具有基础类型W时,并且:

  1. V与W兼容; 要么
  2. V和W具有相同的减少类型.

现在,枚举的基础类型由以下定义:

类型T 的基础类型如下:

  1. 如果T是枚举类型,则其基础类型是枚举定义中声明的基础类型.
  2. [与我们无关]

因此,如果:

enum Foo : int {}
enum Bar : int {}
Run Code Online (Sandbox Code Playgroud)

两者的基本类型都是int.

现在我们可以回到我们的数组元素兼容的定义,看看V和W都是int.由于8.7.1的第一个子弹,类型自身兼容:

当且仅当下列中的至少一个成立时,签名类型T与签名类型U兼容.

  1. T与U相同.

因此,阵列是兼容的.

它们还与底层类型本身的数组兼容:

enum Foo {}
...
object x = new int[10];
Foo[] y = (Foo[]) x; // No exception
Run Code Online (Sandbox Code Playgroud)

注意,x必须在object这里声明为了说服C#编译器这可能是合法的 - 否则它遵循C#语言的规则,这些规则并不那么宽容......


至于你的第二个问题:

找出给定类型(如上面的IsAssignableFrom)或给定实例(如上面的OfType)是否真正实现了IEnumerable的最简单方法是什么?

可以只使用特殊情况数组,因为它们的行为有点奇怪.坦率地说,这可能是最简单的方法.我尝试过使用Type.GetInterfaceMap但也有问题:

未处理的异常:System.ArgumentException:无法重试数组上的通用接口的接口映射.

(是的,最后的拼写错误确实在错误信息中.虽然不能为此提出连接问题而烦恼...)

我强烈怀疑特殊套管是前进的方向......例如,假设你知道你正在处理一个值类型(参考类型数组的协方差是一个单独的问题......)

public static IEnumerable<IEnumerable<T>> OfSequenceType<T>
    (this IEnumerable source) where T : struct
{
    // Nullity check elided...
    foreach (object x in source)
    {
        IEnumerable<T> sequence = x as IEnumerable<T>;
        if (sequence == null)
        {
            continue;
        }
        // Work around odd value type array variance
        Type type = sequence.GetType();
        if (type.IsArray && type.GetElementType() != typeof(T))
        {
            continue;
        }
        yield return sequence;
    }
}
Run Code Online (Sandbox Code Playgroud)