Почему многомерные массивы C # не поддерживают IEnumerable <T>? - PullRequest
62 голосов
/ 08 ноября 2008

Я только что заметил, что многомерный массив в C # не реализует IEnumerable<T>, в то время как он реализует IEnumerable. Для одномерных массивов реализованы как IEnumerable<T>, так и IEnumerable.

Почему эта разница? Если многомерный массив равен IEnumerable, то, конечно, он также должен реализовывать универсальную версию? Я заметил это, потому что я пытался использовать метод расширения для многомерного массива, который не работает, если вы не используете Cast<T> или аналогичный; так что я определенно вижу аргумент в пользу реализации многомерных массивов IEnumerable<T>.

Чтобы уточнить мой вопрос в коде, я ожидал бы, что следующий код напечатает true четыре раза, тогда как на самом деле он печатает true, false, true, true:

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

Ответы [ 6 ]

45 голосов
/ 08 ноября 2008

CLR имеет два различных типа массивов: векторы , которые гарантированно будут одномерными с нижней границей 0, и более общие массивы, которые могут иметь ненулевые границы и ранг, отличный от 0.

Из раздела 8.9.1 спецификации CLI:

Кроме того, создан вектор с Тип элемента T, реализует интерфейс System.Collections.Generic.IList<U> (§8.7), где U: = T.

Я должен сказать, что это кажется мне довольно странным. Учитывая, что он уже реализует IEnumerable, я не понимаю, почему он не должен реализовывать IEnumerable<T>. Не было бы особого смысла в реализации IList<T>, но с простым универсальным интерфейсом все было бы в порядке.

Если вы хотите это, вы можете либо вызвать Cast<T> (если вы используете .NET 3.5), либо написать свой собственный метод для перебора массива. Чтобы избежать приведения, вы должны написать свой собственный метод, который бы находил нижнюю / верхнюю границы каждого измерения и таким образом выбирал вещи. Не очень приятно.

14 голосов
/ 26 июля 2009

Существует обходной путь: вы можете преобразовать любой многомерный массив в IEnumerable

public static class ArrayExtensions
{
    public static IEnumerable<T> ToEnumerable<T>(this Array target)
    {
        foreach (var item in target)
            yield return (T)item;
    }
}
4 голосов
/ 08 ноября 2008

Многомерные массивы являются , а не массивами для целей иерархии наследования. Они совершенно отдельный тип. Кроме того, этот тип не имеет хорошей поддержки от платформы по двум возможным причинам:

  • Это не очень полезно. Единственное реальное использование для многомерных массивов - это матрицы. Для почти всего остального лучше подходят другие структуры данных (например, неровные массивы).
  • Нетрудно разработать общие, полезные методы для этих структур.

В случае IEnumerable как это должно быть реализовано, т. Е. В каком порядке следует перечислять элементы? Нет порядка, присущего многомерным массивам.

2 голосов
/ 29 октября 2017

Одномерные массивы с нулевой привязкой реализуют как IEnumerable, так и IEnumerable<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 голосов
/ 05 декабря 2014

Думайте наоборот. 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 
0 голосов
/ 08 ноября 2008

Зубчатые массивы также не поддерживают 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[]>, это по причинам, указанным в другом ответе, а именно, нет общего способа узнать, какое измерение перебирать. В случае зубчатых массивов не так много места для интерпретации, поскольку синтаксис достаточно ясен, поскольку он представляет собой массив массивов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...