每个枚举数组都实现 IEnumerable。我该如何解决这个问题?

发布于 2024-12-11 01:42:32 字数 1382 浏览 1 评论 0原文

看来,在 .NET 中,“枚举数组”并不是一个强类型的概念。 MyEnum[] 被认为不仅实现了 IEnumerable,还实现了 IEnumerable。 (一开始我也不相信。)

// 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());

因此,当我查看实现 IEnumerable 的所有内容的列表时,我得到了 T[]s 和 List 以及迭代器生成的 IEnumerable ,但我也得到了SomethingElse[]是我不想要的。

确定给定 Type(如上面的 IsAssignableFrom)或给定实例(如上面的 OfType)是否真实可靠的最简单方法是什么 实现,比如说,IEnumerable


我给了 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;
}

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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

渔村楼浪 2024-12-18 01:42:32

我相信这基本上是由于 ECMA 335 第 8.7 节。

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

当且仅当满足以下条件之一时,位置类型 T 才与位置类型 U 兼容。

  1. T 和 U 不是托管指针类型,并且根据第 8.7.1 节中的定义,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 {}

两者的底层类型都是 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

请注意,x 必须在此处声明为 object,以说服 C# 编译器此可能< /em> 是合法的 - 否则它遵循 C# 语言的规则,而 C# 语言的规则不太宽容......


现在关于你的第二个问题:

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

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

未处理的异常:System.ArgumentException:通用接口的接口映射
无法检索数组上的 ace。

(是的,最后的拼写错误确实在错误消息中。不过,我懒得为此提出连接问题...)

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

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;
    }
}

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:

A location type T is compatible-with a location type U if and only if one of the following holds.

  1. T and U are not managed pointer types and T is compatible-with U according to the definition in §8.7.1.

So we look to 8.7.1 and find bullet 5:

T is a zero-based rank-1 array V[], and U is a zero-based rank-1 array W[], and V is array-element-compatible-with W.

So now we're interested in whether the two enum types have an array-element-compatible-with relationship... That then leads to:

A signature type T is array-element-compatible-with a signature type U if and only if T has underlying type V and U has underlying type W and either:

  1. V is compatible-with W; or
  2. V and W have the same reduced type.

Now the underlying type of an enum is defined by this:

The underlying type of a type T is the following:

  1. If T is an enumeration type, then its underlying type is the underlying type declared in the enumeration‘s definition.
  2. [Irrelevant to us]

So in the case of, say:

enum Foo : int {}
enum Bar : int {}

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:

A signature type T is compatible-with a signature type U if and only if at least one of the following holds.

  1. T is identical to U.

Therefore, the arrays are compatible.

They're also compatible with an array of the underlying type itself:

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

Note that x has to be declared as object here 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:

What's the easiest way to find out whether a given Type (as with IsAssignableFrom above) or a given instance (as with OfType above) really and truly implements, say, IEnumerable?

You could just special case arrays, as they behave a little oddly. Frankly that's probably the easiest approach. I tried using Type.GetInterfaceMap but that gives a problem too:

Unhandled Exception: System.ArgumentException: Interface maps for generic interf
aces on arrays cannot be retrived.

(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...)

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;
    }
}
快乐很简单 2024-12-18 01:42:32

我尝试了这个:

var listOfLists = new List<object> {
    new[] { AttributeTargets.All },
    new[] { ConsoleColor.Blue },
    new[] { PlatformID.Xbox, PlatformID.MacOSX },
    new[] { DayOfWeek.Friday, DayOfWeek.Saturday }
};
Console.WriteLine(listOfLists.Where(obj => obj.GetType() == typeof(DayOfWeek[])).Count());

结果我得到了 1。我仍在寻找如何使用 IEnumerable<星期几>在查询中

I tried this:

var listOfLists = new List<object> {
    new[] { AttributeTargets.All },
    new[] { ConsoleColor.Blue },
    new[] { PlatformID.Xbox, PlatformID.MacOSX },
    new[] { DayOfWeek.Friday, DayOfWeek.Saturday }
};
Console.WriteLine(listOfLists.Where(obj => obj.GetType() == typeof(DayOfWeek[])).Count());

I got 1 as a result. I'm still searching to see how you can use IEnumerable< DayOfWeek > in the query

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文