简体   繁体   English

多维数组不实现IEnumerable <T> 或者他们呢?

[英]Multidimensional arrays do not implement IEnumerable<T>, or do they?

For the reasons that I still do not understand ( see this SO question ) multidimensional arrays in CLR do not implement IEnumerable<T> . 由于我仍然不理解的原因( 请参阅此SO问题 )CLR中的多维数组不实现IEnumerable<T> So the following does not compile: 所以以下不编译:

var m = new int[2,2] {{1, 2}, {3, 4}};
var q = from e in m select e;

Then how come that this works just fine in VB.NET ? 那怎么会在VB.NET中运行得很好呢?

Sub Main()
    Dim m(,) As Integer = {{1, 2}, {3, 4}}
    Dim q = From e In m Select e

    For Each i In q
        Console.WriteLine(i)
    Next
End Sub

Update: 更新:

The following code works because the C# compiler replaces the foreach with for loops to go through each dimension. 以下代码有效,因为C#编译器将foreach替换for循环以遍历每个维度。

foreach(var e in m)
    Console.WriteLine(e);

becomes

int[,] numArray3 = new int[,] { { 2, 2 }, { 3, 3 } };
int upperBound = numArray3.GetUpperBound(0);
int num4 = numArray3.GetUpperBound(1);
for (int i = numArray3.GetLowerBound(0); i <= upperBound; i++)
{
    for (int j = numArray3.GetLowerBound(1); j <= num4; j++)
    {
        int num = numArray3[i, j];
        Console.WriteLine(num);
    }
}

The query works in VB.Net because it gets transformed into 该查询在VB.Net中工作,因为它被转换为

IEnumerable<object> q = m.Cast<object>().Select<object, object>(o => o);

This works because you can call Cast<TResult> () on IEnumerable , which [*,*] implements. 这是有效的,因为你可以在IEnumerable上调用Cast<TResult> (), [*,*]实现。

The LINQ query doesn't work in C# because of the different approach the C# and VB.Net designers took. LINQ查询在C#中不起作用,因为C#和VB.Net设计者采用了不同的方法。 VB.Net takes a more hand holding approach and fixes your mistake and converts IEnumerable to IEnumerable<object> so it can be used. VB.Net采用更多的手持方法并修复您的错误并将IEnumerable转换为IEnumerable<object>以便可以使用它。

In C#, you can simulate this by using 在C#中,您可以通过使用来模拟它

var q = from e in m.Cast<object>() select e;

There are two reasons they don't implement it natively in C#: 他们没有在C#中原生实现它有两个原因:

  • There's more than one way you could do it. 你可以做多种方式。 Do you want each 'cell', or do you want each 'row'? 你想要每个'细胞',还是你想要每个'行'? And how do you define 'row': [], IEnumerable, other? 你如何定义'row':[],IEnumerable,other? What if there are more than two dimensions? 如果有两个以上的尺寸怎么办? As soon as they pick one way, an army of developers will tell them they should have done it a different way. 一旦他们选择了一种方式,一大批开发人员会告诉他们他们应该以不同的方式做到这一点。
  • Thanks to iterator blocks and the yield keyword, it just so easy to implement your own that's specific to your need at the time. 感谢迭代器块和yield关键字,实现您自己的特定于当时的需求非常容易。 Of course, that's a C# construct, but it's not that much harder in VB. 当然,这是一个C#构造,但它在VB不是那么困难得多。

The MSDN page for Array includes this: ArrayMSDN页面包括:

Important Note: In the .NET Framework version 2.0, the Array class implements the System.Collections.Generic.IList<T>, System.Collections.Generic.ICollection<T>, and System.Collections.Generic.IEnumerable<T> generic interfaces. 重要说明:在.NET Framework 2.0版中,Array类实现System.Collections.Generic.IList <T>,System.Collections.Generic.ICollection <T>和System.Collections.Generic.IEnumerable <T> generic接口。 The implementations are provided to arrays at run time, 这些实现在运行时提供给数组,

Note the final words in the quote... it appears this generation does not happen for multi-dimensional arrays (so a documentation bug). 注意引用中的最后一个单词......看起来这一代不会发生在多维数组中(所以文档错误)。

But as others have noted, what would T be? 但正如其他人所指出的那样, T会是什么? A good case can be made for T[] (or, these days with LINQ, IEnumerable<T> ). 可以为T[]做一个很好的案例(或者,现在使用LINQ, IEnumerable<T> )。

In the end, if you want to iterate all the array's members just stick with IEnumerable and Cast<T> extension. 最后,如果你想迭代所有数组的成员,只需坚持使用IEnumerable和Cast <T>扩展名。 Otherwise easy to write your own. 否则很容易自己写。

Tip: instead of Cast<object>() use a typed range variable 提示:使用类型化范围变量而不是Cast <object>()


Samuel stated: 塞缪尔说:

In C#, you can simulate this by using 在C#中,您可以通过使用来模拟它

 var q = from e in m.Cast<object>() select e; // q is of type IEnumerable<object> 

which is of course correct as far as mimicking VB in C# is concerned, but you would loose your type information. 就C#中模仿VB来说当然是正确的,但是你会丢失你的类型信息。 Instead, it is much easier and as it turns out, slightly better readable, to simply declare your range variable. 相反,它简单地声明你的范围变量,更简单,更简单易读。

The following compiles, performs better, is type safe and does not loose type information: 以下编译,执行得更好,类型安全且不会丢失类型信息:

var m = new int[2, 2] { { 1, 2 }, { 3, 4 } };
var q = from int e in m select e;
// q is of type IEnumerable<int>

In the original suggestion, you would have an IEnumerable<object> , using int e you change that into IEnumerable<int> , which has its advantages. 在最初的建议中,你将拥有一个IEnumerable<object> ,使用int e你改成IEnumerable<int> ,这有其优点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM