为什么C#多维数组不实现IEnumerable <T>?


70

我刚刚注意到C#中的多维数组没有实现IEnumerable<T>,但是实现了IEnumerable。对于一维数组,这两个IEnumerable<T>IEnumerable来实现。

为什么会有这种差异?如果是多维数组IEnumerable,那么肯定也应该实现通用版本吗?我注意到这一点是因为我尝试对多维数组使用扩展方法,除非您使用Cast<T>或类似方法,否则扩展方法将失败。所以我绝对可以看到使多维数组实现的参数IEnumerable<T>

为了澄清我的问题在代码中,我希望下面的代码打印true的四倍,而它实际打印truefalsetruetrue

int[] singleDimensionArray = new int[10];
int[,] multiDimensional = new int[10, 10];

Debug.WriteLine(singleDimensionArray is IEnumerable<int>);
Debug.WriteLine(multiDimensional is IEnumerable<int>);
Debug.WriteLine(singleDimensionArray is IEnumerable);
Debug.WriteLine(multiDimensional is IEnumerable);

4
也很丑陋:multiDimensional可隐式转换为非泛型类型System.Collections.IList(仅因为System.Array实现了该接口)。所以你可以说System.Collections.IList mdCast = multiDimensional;。然后,mdCast仅在运行时使用单参数索引器会失败。请参阅MSDN上的文档。注意异常类型ArgumentException。真丑。
Jeppe Stig Nielsen

Answers:


49

CLR具有两种不同类型的数组:保证为一维且下界为0的向量,以及更通用的数组,其可具有非零界限且秩为非0。

根据CLI规范的8.9.1节:

此外,创建的元素类型为T的向量将实现接口 System.Collections.Generic.IList<U> (§8.7),其中U:=T。

我不得不说这对我来说似乎很奇怪。鉴于它已经实现,IEnumerable我不知道为什么不应该实现IEnumerable<T>。实施起来没有多大意义IList<T>,但是简单的通用接口就可以了。

如果需要,可以调用Cast<T>(如果使用的是.NET 3.5),也可以编写自己的方法来遍历数组。为了避免转换,您必须编写自己的方法,该方法可以找到每个维度的上下边界,并以这种方式获取内容。不太愉快。


同样,C#语言规范(版本4.0)在第6.1.6段中提及了将一维数组转换IList<>为其基本接口的过程。遗憾的是aT[,]不是ICollection<T>
Jeppe Stig Nielsen 2012年

16

有一种解决方法:您可以将任何多维数组转换为IEnumerable

public static class ArrayExtensions
{
    public static IEnumerable<T> ToEnumerable<T>(this Array target)
    {
        foreach (var item in target)
            yield return (T)item;
    }
}

好东西!我必须明确地指定通用类型,因此用法变为:myArray.ToEnumerable <
myType

36
这与相同IEnumerable.Cast<T>
Markus Jarderot

@Markus Brilliant,这应该是公认的答案。顺便说一句,如果数组可以包含空值,它将变为Matrix.Cast(Of T).Where(Function(x)x IsNot Nothing)
smirkingman


1
IEnumerator正如@SergeyTeplyakov在回答中所说,这将访问导致装箱的数组。如果显式键入数组(,等)T[,],则装箱不会发生T[,,]
米罗斯拉夫·波利基

4

零界一维数组既可实现IEnumerableIEnumerable<T>,也可实现多维数组,但仅可实现IEnumerable。@Jader Dias的“解决方法”的确将多维数组转换为,IEnumerable<T>但是成本很高:数组的每个元素都将被装箱。

这是一个不会对每个元素进行装箱的版本:

public static class ArrayExtensions
{
    public static IEnumerable<T> ToEnumerable<T>(this T[,] target)
    {
        foreach (var item in target)
            yield return item;
    }
}

0

锯齿状数组也不支持IEnumerable<int>,因为多维结构并不是真正的类型数组,它们是类型数组的数组:

int[] singleDimensionArray = new int[10];
int[][] multiJagged = new int[10][];

Debug.WriteLine(singleDimensionArray is IEnumerable<int>);
Debug.WriteLine(multiJagged is IEnumerable<int[]>);
Debug.WriteLine(singleDimensionArray is IEnumerable);
Debug.WriteLine(multiJagged is IEnumerable);

打印true,true,true,true。

注意int[,]不是IEnumerable<int[]>,这是由于另一个答案中指定的原因,即没有通用的方法可以知道要迭代哪个维度。对于锯齿状数组,没有太多解释空间,因为关于数组是数组的语法非常清楚。


递归不在讨论中。
递归

@递归:欢呼,固定。仅是混淆我的术语的情况。
马修·沙利

3
他没有谈论锯齿状阵列,而是谈论了多维阵列。
Felix K.

0

反过来思考。2d数组已存在。只是列举一下。创建具有初始数组或标记的分数和位置的二维数组,包括重复值。

int[] secondmarks = {20, 15, 31, 34, 35, 50, 40, 90, 99, 100, 20};

IEnumerable<int> finallist = secondmarks.OrderByDescending(c => c);

int[,] orderedMarks = new int[2, finallist.Count()];

Enumerable.Range(0, finallist.Count()).ToList().ForEach(k => {orderedMarks[0, k] = (int) finallist.Skip(k).Take(1).Average();
orderedMarks[1, k] = k + 1;}); 

Enumerable.Range(0, finallist.Count()).Select(m => new {Score = orderedMarks[0, m], Place = orderedMarks[1, m]}).Dump();

结果:

Score Place

100     1
99      2 
90      3 
50      4 
40      5 
35      6    
34      7    
31      8    
20      9     
20     10 
15     11 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.