【问题标题】:Why do C# multidimensional arrays not implement IEnumerable<T>?为什么 C# 多维数组不实现 IEnumerable<T>?
【发布时间】:2010-09-21 11:08:00
【问题描述】:

我刚刚注意到 C# 中的多维数组没有实现IEnumerable&lt;T&gt;,但它实现了IEnumerable。对于一维数组,IEnumerable&lt;T&gt;IEnumerable都实现了。

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

为了澄清我在代码中的问题,我希望以下代码打印四次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);

【问题讨论】:

  • 也很难看:multiDimensional 可以隐式转换为 non-generic 类型 System.Collections.IList(仅仅是因为 System.Array 实现了该接口)。所以你可以说System.Collections.IList mdCast = multiDimensional;。然后在mdCast 上使用单参数索引器只会在运行时失败。见doc on MSDN。请注意异常类型ArgumentException。真的很丑。

标签: c# .net arrays multidimensional-array


【解决方案1】:

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

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

打印真实,真实,真实,真实。

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

【讨论】:

  • 递归不在讨论中。
  • @recursive:干杯,已修复。只是混淆了我的术语。
  • 他说的不是锯齿数组,他说的是多维数组。
【解决方案2】:

CLR 有两种不同类型的数组:vectors 保证是一维的,下界为 0,更一般的数组可以有非零边界和其他秩大于 0。

来自 CLI 规范的第 8.9.1 节:

另外,一个创建的向量 元素类型 T,实现 界面 System.Collections.Generic.IList&lt;U&gt; (第 8.7 节),其中 U := T。

我不得不说这对我来说似乎很奇怪。鉴于它已经实现了IEnumerable,我不明白为什么它不应该实现IEnumerable&lt;T&gt;。实现IList&lt;T&gt; 没有多大意义,但简单的通用接口就可以了。

如果需要,您可以调用 Cast&lt;T&gt;(如果您使用的是 .NET 3.5)或编写自己的方法来遍历数组。为了避免强制转换,您必须编写自己的方法来找到每个维度的下限/上限,并以这种方式获取内容。不是很愉快。

【讨论】:

  • 此外,C# 语言规范(4.0 版)在第 6.1.6 段中提到了将一维数组转换为 IList&lt;&gt; 及其基本接口。但是很遗憾T[,] 不是ICollection&lt;T&gt;
【解决方案3】:

有一个解决方法:您可以将任何多维数组转换为 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()
  • 这和IEnumerable.Cast&lt;T&gt;一样。
  • @Markus Brilliant,这应该是公认的答案。顺便说一句,如果数组可以包含空值,它将变为 Matrix.Cast(Of T).Where(Function(x) x IsNot Nothing)
  • @smirkingman 或IEnumerable.OfType&lt;T&gt;
  • 这会通过IEnumerator 访问数组,这会导致装箱,正如@SergeyTeplyakov 在他的回答中所说的那样。如果数组被显式键入(T[,]T[,,] 等),则不会发生装箱。
【解决方案4】:

反过来想。二维数组已经存在。列举一下就行了。创建一个二维数组,其中包含初始数组或标记的分数和位置,包括重复值。

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 

【讨论】:

    【解决方案5】:

    零界一维数组实现了IEnumerableIEnumerable&lt;T&gt;,但不幸的是,多维数组只实现了IEnumerable。 @Jader Dias 的“解决方法”确实将多维数组转换为 IEnumerable&lt;T&gt;,但代价高昂:数组的每个元素都将被装箱。

    这是一个不会导致每个元素都装箱的版本:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-04
      • 2011-02-15
      • 2011-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-20
      相关资源
      最近更新 更多