IEnumerable< Object>からのキャストIEnumerable< string>へ
-
03-07-2019 - |
質問
最近、c#で非常に驚くべき動作を発見しました。
パラメータとして IEnumerable< Object>
をとるメソッドがあり、渡していました
IEnumerable< string>
しかし、それは不可能です。
C#では、これが不可能な理由よりもすべてをObjectにアップキャストできますか?
それは私にとって完全に混乱しています。
この問題について誰かが私をクリアしてください。
解決
これの専門用語は、ジェネリックはC#3.0以前では不変であるということです。 C#4.0以降、キャストは機能します。
不変式とは、2つのジェネリック型のパラメータが関連している(つまり、互いにサブタイプまたはスーパータイプである)ため、2つのジェネリック型の間に関係がないことを意味します。
あなたの例では、 IEnumerable< object>
と IEnumerable< string>
の間に型の関係はありません。これは、stringがオブジェクトのサブタイプだからです。これらは、文字列とintのように、まったく関係のない2つの型と見なされています(これらは両方ともオブジェクトのサブタイプですが、すべてがそうです)
この問題に遭遇したいくつかの回避策と例外があります。
最初に、各文字列を個別にオブジェクトにキャストできます。.NET3.0を使用している場合は、 Cast< T>()
拡張メソッドを使用して実行できます。それ以外の場合は、foreachを使用して、結果を必要な静的型の新しい変数に入れることができます。
次に、配列は参照型の例外です。つまり、string []型をobject []型を受け入れるメソッドに渡すことは機能します。
他のヒント
他の人が指摘したように、ジェネリック型は不変です。 IEnumerable< T>
は共変である可能性がありますが、C#は現在、変種の指定をサポートしていません。 C#4.0はバリアントをサポートする予定であるため、今後サポートされる可能性があります。
これを回避するには、LINQ拡張メソッド Cast< object>()
を使用します。 IEnumerable< object>>
を取る Foo
というメソッドがあると仮定します。このように呼び出すことができます
Foo(stringEnumerable.Cast<object>());
IEnumerable&lt; string&gt;
を IEnumerable&lt; object&gt;
を必要とする関数に渡す最も簡単な方法は、次のような関数を変換することです。
public IEnumerable<object> convert<T>(IEnumerable<T> enumerable)
{
foreach (T o in enumerable)
yield return o;
}
C#4がリリースされたとき、共分散と反分散をサポートするため、これは必要ありません。
使用する必要があります
IEnumerable<T>
異なる型を渡したい場合は、Tを照会して、どの型かを調べることができます。
これについて考える良い方法は、「これができたらどうなりますか?」と自問することです。次の例をご覧ください。
IEnumerable<String> strings=...;
IEnumerable<Object> objects = strings; // assume this were legal
objects.Add(new Integer(5)); // what the...
文字列のリストに整数を追加しました。コンパイラは、型の安全性を保つためにこれを行います。