webdevqa.jp.net

C#多次元配列がIEnumerable <T>を実装しないのはなぜですか?

C#の多次元配列はIEnumerable<T>を実装していませんが、IEnumerableは実装しています。 1次元配列の場合、IEnumerable<T>IEnumerableの両方が実装されています。

なぜこの違いがあるのですか?多次元配列がIEnumerableの場合、確かにジェネリックバージョンも実装する必要がありますか?多次元配列で拡張メソッドを使用しようとしたため、Cast<T>などを使用しないと失敗するため、このことに気付きました。多次元配列にIEnumerable<T>を実装させるための議論は間違いなくわかります。

コードで私の質問を明確にするために、私は次のコードがtruetruefalsetrueを実際に出力するのに対し、trueを4回出力することを期待します。

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

CLRには2種類の配列があります。vectorsは下限が0の1次元であることが保証されており、より一般的な配列は非ゼロの境界と0以外のランクを持つことができます。

CLI仕様のセクション8.9.1から:

さらに、エレメントタイプTで作成されたベクターは、インターフェイスSystem.Collections.Generic.IList<U>(§8.7)を実装します。ここで、U:= Tです。

私にはかなり奇妙に思えます。 IEnumerableがすでに実装されていることを考えると、IEnumerable<T>を実装してはならない理由がわかりません。 IList<T>を実装することはそれほど意味がありませんが、単純な汎用インターフェースで十分です。

これが必要な場合は、Cast<T>(.NET 3.5を使用している場合)を呼び出すか、独自のメソッドを記述して配列を反復処理できます。キャストを回避するには、各次元の下限/上限を見つけ、その方法でフェッチする独自のメソッドを記述する必要があります。ひどく快適ではありません。

46
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

多次元配列は、継承階層を目的としたnot配列です。それらは完全に別のタイプです。さらに、このタイプには、2つの考えられる理由により、フレームワークからの適切なサポートがありません。

  • 実際にはそれほど役に立ちません。多次元配列の唯一の実際の用途は行列です。それ以外のほとんどの場合、他のデータ構造(ギザギザの配列など)が適しています。
  • これらの構造に対して一般的で有用な方法を考案することは簡単ではありません。

IEnumerableの場合、これをどのように実装する必要がありますか、つまり、要素を列挙する順序は?多次元配列に固有の順序はありません。

4
Konrad Rudolph

ゼロバインドの1次元配列はIEnumerableIEnumerable<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

ギザギザの配列も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[]>ではありません。これは、他の回答で指定されている理由によるものです。つまり、どのディメンションを反復するかを知る一般的な方法はありません。ギザギザの配列では、配列の配列であることについて構文がかなり明確であるため、解釈の余地はあまりありません。

0

逆に考えます。 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 
0