Многомерные массивы не реализуют IEnumerable <T>, или они?
По причинам, которые я до сих пор не понимаю (см. этот вопрос SO), многомерные массивы в CLR не реализуют IEnumerable<T>
. Итак, следующее не компилируется:
var m = new int[2,2] {{1, 2}, {3, 4}};
var q = from e in m select e;
Тогда как получилось, что этот отлично работает в 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:
Следующий код работает, потому что компилятор С# заменяет циклы foreach
на for
для прохождения каждого измерения.
foreach(var e in m)
Console.WriteLine(e);
становится
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);
}
}
Ответы
Ответ 1
Запрос работает в VB.Net, потому что он преобразуется в
IEnumerable<object> q = m.Cast<object>().Select<object, object>(o => o);
Это работает, потому что вы можете вызвать Cast<TResult>
() на IEnumerable
, который реализует [*,*]
.
Запрос LINQ не работает на С# из-за другого подхода, который взяли дизайнеры С# и VB.Net. VB.Net использует более удобный подход и исправляет вашу ошибку и преобразует IEnumerable
в IEnumerable<object>
, чтобы его можно было использовать.
В С# вы можете имитировать это, используя
var q = from e in m.Cast<object>() select e;
Ответ 2
Есть две причины, по которым они не реализуют его в С#:
- Там вы можете сделать несколько способов. Вы хотите, чтобы каждая "ячейка", или вам нужна каждая "строка"? И как вы определяете "row": [], IEnumerable, другое? Что делать, если есть более двух измерений? Как только они выберут один путь, армия разработчиков скажет им, что они должны были сделать это по-другому.
- Благодаря блокам итератора и ключевому слову
yield
, это просто так легко реализовать в соответствии с вашими потребностями в то время. Конечно, это С# -конструкция, но это не намного сложнее в VB.
Ответ 3
Страница MSDN для Array включает в себя следующее:
Важное примечание:В .NET Framework версии 2.0 класс Array реализует универсальные интерфейсы System.Collections.Generic.IList <T> , System.Collections.Generic.ICollection <T> и System.Collections.Generic.IEnumerable <T> . Реализации предоставляются массивам во время выполнения,
Обратите внимание на последние слова в цитате... похоже, что это поколение не происходит для многомерных массивов (так что ошибка документации).
Но, как отметили другие, что бы было T
быть? Хороший пример можно сделать для T[]
(или в наши дни с LINQ, IEnumerable<T>
).
В конце концов, если вы хотите итерации всех членов массива, просто придерживайтесь расширения IEnumerable и Cast <T> . В противном случае легко написать свой собственный.
Ответ 4
Совет: вместо Cast <object> () используйте типизированную переменную диапазона
Самуил заявил:
В С# вы можете имитировать это, используя
var q = from e in m.Cast<object>() select e;
// q is of type IEnumerable<object>
который, конечно, правильный, если имитировать VB в С#, но вы потеряете информацию о типе. Вместо этого, это намного проще и, как оказалось, немного лучше читаемо, просто объявить переменную диапазона.
Следующие компиляции, выполняемые лучше, безопасны по типу и не теряют информацию о типе:
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>
В исходном предложении у вас будет IEnumerable<object>
, используя int e
, вы измените это на IEnumerable<int>
, у которого есть свои преимущества.