C# では、List<string> オブジェクトを List<object> 変数に格納できないのはなぜですか
-
08-06-2019 - |
質問
C# では List オブジェクトを List 変数に格納できず、そのように明示的にキャストすることもできないようです。
List<string> sl = new List<string>();
List<object> ol;
ol = sl;
暗黙的に型を変換できませんという結果になります System.Collections.Generic.List<string>
に System.Collections.Generic.List<object>
その後...
List<string> sl = new List<string>();
List<object> ol;
ol = (List<object>)sl;
型を変換できませんという結果になります System.Collections.Generic.List<string>
に System.Collections.Generic.List<object>
もちろん、文字列リストからすべてを取り出して、一度に 1 つずつ戻すことによってこれを行うこともできますが、これはかなり複雑な解決策になります。
解決
このように考えてください。このようなキャストを実行してから Foo 型のオブジェクトをリストに追加すると、文字列のリストは一貫性がなくなります。最初の参照を反復すると、Foo インスタンスに到達すると Foo を文字列に変換できないため、クラス キャスト例外が発生します。
余談ですが、リバースキャストができるかどうかの方が重要だと思います。
List<object> ol = new List<object>();
List<string> sl;
sl = (List<string>)ol;
私はしばらく C# を使用していないので、それが合法かどうかはわかりませんが、その種のキャストは実際に (潜在的に) 便利です。この場合、より一般的なクラス (オブジェクト) から、一般的なクラスを拡張したより具体的なクラス (文字列) に移行します。この方法では、文字列のリストに追加しても、オブジェクトのリストに違反することはありません。
そのようなキャストが C# で正当であるかどうかを知っている人、またはテストできる人はいますか?
他のヒント
.NET 3.5 を使用している場合は、Enumerable.Cast メソッドを見てください。これは拡張メソッドなので、List 上で直接呼び出すことができます。
List<string> sl = new List<string>();
IEnumerable<object> ol;
ol = sl.Cast<object>();
それはあなたが要求したものと正確には異なりますが、うまくいくはずです。
編集:Zooba が指摘したように、ol.ToList() を呼び出してリストを取得できます。
型パラメーターが異なるジェネリック型間でキャストすることはできません。特殊化されたジェネリック型は同じ継承ツリーの一部を形成しないため、無関係な型になります。
NET 3.5 より前でこれを行うには:
List<string> sl = new List<string>();
// Add strings to sl
List<object> ol = new List<object>();
foreach(string s in sl)
{
ol.Add((object)s); // The cast is performed implicitly even if omitted
}
Linq の使用:
var sl = new List<string>();
// Add strings to sl
var ol = new List<object>(sl.Cast<object>());
// OR
var ol = sl.Cast<object>().ToList();
// OR (note that the cast to object here is required)
var ol = sl.Select(s => (object)s).ToList();
その理由は、次のようなジェネリッククラスであるためです。 List<>
ほとんどの場合、外部では通常のクラスとして扱われます。例えばあなたが言う時 List<string>()
コンパイラは言う ListString()
(文字列が含まれています)。[技術者:これは、何が起こっているかを非常にわかりやすく英語化したものです]
したがって、明らかにコンパイラは、内部コレクションの項目をキャストして ListString を ListObject に変換するほど賢くはありません。
そのため、Convert() のような IEnumerable の拡張メソッドがあり、コレクション内に格納されている項目の変換を簡単に提供できます。これは、ある項目から別の項目へのキャストと同じくらい簡単です。
これは共分散と多くの関係があります。たとえば、ジェネリック型はパラメーターとみなされ、パラメーターがより具体的な型に適切に解決されない場合、操作は失敗します。このような意味は、オブジェクトのようなより一般的な型にキャストすることは実際にはできないということです。Rex が述べたように、List オブジェクトは各オブジェクトを変換しません。
代わりに ff コードを試してみることをお勧めします。
List<string> sl = new List<string>();
//populate sl
List<object> ol = new List<object>(sl);
または:
List<object> ol = new List<object>();
ol.AddRange(sl);
ol は (理論的には) sl のすべての内容を問題なくコピーします。
はい、.NET 3.5 からは次のことができます。
List<string> sl = new List<string>();
List<object> ol = sl.Cast<object>().ToList();
Mike - C# でも反変性は許されないと思います
見る CLR のジェネリック型パラメーターの差異 さらに詳しい情報については。
これ(反変性)は実際には C# 4.0 でサポートされると思います。http://blogs.msdn.com/charlie/archive/2008/10/27/linq-farm-covariance-and-contravariance-in-visual-studio-2010.aspx
これは実際には、「ol」リストのバリアントに奇妙な「オブジェクト」を入れようとしないためです( List<object>
許可するようです) - その場合、コードがクラッシュするためです(リストは実際には List<string>
文字列型オブジェクトのみを受け入れます)。そのため、変数をより一般的な仕様にキャストすることはできません。
Java ではその逆で、ジェネリックスはなく、代わりに実行時にはすべてがオブジェクトのリストになり、厳密に型指定されたはずのリストに奇妙なオブジェクトを実際に詰め込むことができます。「Reified generics」を検索すると、Java の問題に関する幅広い議論が表示されます...
ジェネリックスでのこのような共分散はサポートされていませんが、実際には配列を使用してこれを行うことができます。
object[] a = new string[] {"spam", "eggs"};
C# は実行時チェックを実行して、たとえば、 int
の中へ a
.
ここでは、コンテンツを暗黙的にキャストできる IList に対する .NET 3.5 以前の別のソリューションを示します。
public IList<B> ConvertIList<D, B>(IList<D> list) where D : B
{
List<B> newList = new List<B>();
foreach (D item in list)
{
newList.Add(item);
}
return newList;
}
(Zoobaの例に基づく)
私は次のものを持っています:
private List<Leerling> Leerlingen = new List<Leerling>();
そして、私はそれを収集したデータで埋めるつもりでした List<object>
最終的に私にとってうまくいったのはこれです:
Leerlingen = (List<Leerling>)_DeserialiseerLeerlingen._TeSerialiserenObjecten.Cast<Leerling>();
.Cast
取得したいタイプに合わせてください IEnumerable
その型から型キャストして、 IEnemuerable
に List<>
あなたが欲しいのです。
うーん、前のコメントのおかげで、それを見つける方法が 2 つ見つかりました。最初の方法では、要素の文字列リストを取得し、それを IEnumerable オブジェクト リストにキャストします。
IEnumerable<object> ob;
List<string> st = new List<string>();
ob = st.Cast<object>();
2 つ目は、IEnumerable オブジェクト タイプを回避し、文字列をオブジェクト タイプにキャストし、同じ文内で関数 "toList()" を使用するだけです。
List<string> st = new List<string>();
List<object> ob = st.Cast<object>().ToList();
私は後者のほうが好きです。これがお役に立てば幸いです。
List<string> sl = new List<string>();
List<object> ol;
ol = new List<object>(sl);