技術的には、2 種類の配列があります。ベクトル型または行列型。ランタイムは Vector 型を次のように参照しSz_Array
ます。これらは、1d 配列を宣言したときに得られる型です*。理由はわかりません。行列型は多次元配列を表します。残念ながら、どちらも継承元でArray
あり、他の中間型はありません。
Array クラスがそのインデクサーを直接公開しないのはなぜですか?
1 次元配列のインデクサーを持っている場合にのみアクセスできる理由は、T[]
1 次元配列のインデクサーが IL オペコードを介してランタイムに実装されているためです。
例えば
static T GetFirst<T>(T[] a)
{
return a[0]:
}
次の il:: に変換します。
L_0000: ldarg.0
L_0001: ldc.i4.0
L_0002: ldelem.any !!T
L_0007: ret
次のC#として
private static T GetFirst<T>(IList<T> a)
{
return a[0];
}
この IL に変換されます。
L_0000: ldarg.0
L_0001: ldc.i4.0
L_0002: callvirt instance !0 [mscorlib]System.Collections.Generic.IList`1<!!T>::get_Item(int32)
L_0007: ret
したがって、1 つは opcode を使用してldelem.any
おり、もう1 つはcallvirt
メソッドを使用していることがわかります。
ランタイムIList<T>,IEnumerable<T>
は、実行時に配列を挿入します。MSIL でのそれらのロジックは、クラスにあります。SZArrayHelper
実装を提供するランタイムは、生成された配列ごとに 2 つのヘルパー メソッドも作成し、インデクサーをサポートしていない言語 (そのような言語が存在する場合) をサポートします。C# はそれらを公開しませんが、呼び出すのに有効なメソッドです。彼らです:
T Get(Int32)
void Set(Int32, T)
これらのメソッドは、次元に応じて行列型の配列に対しても生成され、インデクサーを呼び出すときに C# によって使用されます。
ただし、型付き配列であることを実際に指定しない限り、インデクサーは取得されません。Array にはインデクサー メソッドがありません。コンパイル時に問題の配列がベクトル型であり、配列の要素型であることを知る必要があるため、オペコードは使用できません。
しかし、Array は IList を実装しています! それはインデクサーを持っています。私はそれを呼び出すことができませんか?
はい、そうですが、IList メソッドの実装は明示的な実装であるため、キャスト時または一般的な制約によってバインドされたときにのみ C# で呼び出すことができます。おそらく、ベクトル型以外の配列の場合、そのメソッドのいずれかを呼び出すと、サポートされていない例外がスローされるためです。条件付きでのみサポートされているため、ランタイムの作成者は、これが 1 次元配列であることがわかっている場合にキャストすることをおそらく望んでいますが、今は型に名前を付けることはできません。
IList
多次元配列の実装がサポートされていない場合、配列が実装されるのはなぜですか?
これはおそらく 1.0 からあった間違いです。何らかの理由で誰かが多次元配列を にキャストしている可能性があるため、今は修正できませんIList
。2.0 では、ランタイム マジックを追加して、実行時にベクター型クラスに実装を追加し、1 次元配列のみがIList<T>
and を実装するようにしIEnumerable<T>
ました。
そして、次の質問を補間することができます::
キャストせずにインデクサーを表示するにはどうすればよいですか? メソッドのシグネチャを次のように変更します::
public static T[] ToArray<T>(this T source)
しかし、あなたの ToArray は T[] を返さず、それ以外の何かを返すと言うかもしれません。戻り値の型を明示的に指定できる場合は、それを行ってください。参照の種類である場合は、いつでも配列の共分散を悪用して戻り値の型を変更object[]
できますが、ArrayTypeMismatchException に翻弄されます。そのキャストが不正であるため、valuetype を取得している場合、これは機能しません。この時点で戻ることができIList
ますが、要素をボックス化していて、依然として ArrayTypeMismatchException に翻弄されています。Array は、すべての配列タイプの基本クラスです。その理由は、次のようなコンテンツにアクセスするのに役立つヘルパー メソッドがあるからですGetValue
。SetValue
また、1d 配列だけでなく Nd の要素にもアクセスできるように、インデックスの配列を取るオーバーロードがあることに気付くでしょう。例えば
IList myArray = ToArray(myValues);
// myArray is actually a string array.
myArray[0]='a';
// blows up here as you can't explicitly cast a char to a string.
つまり、明示的な型がわからないということです。そして、Array
implementationのすべての継承者はIList
、あまり意味がない場合でも、1.0 から存在していたので変更できない実装の詳細です。
- 技術的には、これは 100% 真実ではありません。1 次元のみの行列型配列を作成できます。
typeof(int).MakeArrayType(1)
このエルドリッチの忌まわしきものは IL で作成するか、 を使用してタイプを作成できますSystem.Int32[*]
。System.Int32[]