it-swarm.com.ru

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

Я только что заметил, что многомерный массив в 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);
62
driis

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

45
Jon Skeet

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

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

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

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

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

4
Konrad Rudolph

Ограниченные нулями одномерные массивы реализуют и 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;
    }
}
2
Sergey Teplyakov

Зубчатые массивы также не поддерживают 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);

Печатает правда, правда, правда, правда.

Примечание: int[,] не является IEnumerable<int[]>, это по причинам, указанным в другом ответе, а именно, нет общего способа узнать, какое измерение перебирать. С неровными массивами не так много места для интерпретации, потому что синтаксис достаточно ясен, поскольку он представляет собой массив массивов.

0
Matthew Scharley

Думай наоборот. 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
PEDRO ACOSTA MOLINA