Подтвердить что ты не робот

Почему С# многомерные массивы не реализуют IEnumerable <T>?

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

Почему это различие? Если многомерный массив равен IEnumerable, он также должен реализовать универсальную версию? Я заметил это, потому что я пытался использовать метод Extension для многомерного массива, который терпит неудачу, если вы не используете 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);
4b9b3361

Ответ 1

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), либо написать свой собственный метод для итерации по массиву. Чтобы избежать кастинга, вам придется написать свой собственный метод, который нашел бы нижнюю/верхнюю границу каждого измерения и таким образом набирал вещи. Не очень приятно.

Ответ 2

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

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

Ответ 3

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

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

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

Ответ 4

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

Ответ 5

Одномерные массивы с нулевой границей реализуют как 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;
    }
}

Ответ 6

Думайте наоборот. 2d-массив уже существует. Просто перечислите его. Создайте 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