C#で、List <string>オブジェクトをList <object>変数に格納できないのはなぜですか


85

ListオブジェクトをC#の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つずつ戻すことでそれを行うことができますが、それはかなり複雑な解決策です。


5
これはC#4.0で変更されるため、共変性と反変性を検索することをお勧めします。それはタイプセーフな方法でそのようなことを可能にします。
John Leidegren 2009年

多かれ少なかれ重複:stackoverflow.com/questions/317335/...
GOジョエル

7
John> C#4はこれを許可しません。ol.Add(new object());について考えてみてください。
ギヨーム

ここで
JonSkeet

回答:


39

このように考えると、そのようなキャストを行ってから、タイプFooのオブジェクトをリストに追加すると、文字列のリストに一貫性がなくなります。最初の参照を繰り返すと、Fooインスタンスにヒットすると、Fooを文字列に変換できなかったため、クラスキャスト例外が発生します。

ちなみに、リバースキャストができるかどうかはもっと重要だと思います。

List<object> ol = new List<object>();
List<string> sl;
sl = (List<string>)ol;

私はしばらくC#を使用していないので、それが合法かどうかはわかりませんが、そのようなキャストは実際には(潜在的に)便利です。この場合、より一般的なクラス(オブジェクト)から、一般的なクラスから拡張されたより具体的なクラス(文字列)に移行します。このように、文字列のリストに追加しても、オブジェクトのリストに違反することはありません。

そのようなキャストがC#で合法であるかどうかを誰かが知っているか、テストできますか?


4
あなたの例も同じ問題を抱えていると思います。olに文字列ではないものが含まれているとどうなりますか?問題は、追加/挿入など、リストの一部のメソッドが正常に機能することです。しかし、反復は実際の問題かもしれません。
Chris Ammerman

3
Eric Lippertは、このトピックに関する一連のすばらしいブログ投稿を公開しています。ジェネリックメソッドに共変性と反変性の制約を追加するのに効果があるのに、クラスレベルでは期待どおりに機能しないのはなぜですか。is.gd / 3kQc
Chris Ammerman

@ChrisAmmerman:ol文字列ではないものが含まれている場合、実行時にキャストが失敗することを期待していると思います。しかし、あなたが本当にトラブルに実行する場所のキャストが成功した場合で、かつ、その後に何かが追加されたolことは、文字列ではありません。sl同じオブジェクトを参照するため、List<string>文字列以外が含まれるようになります。これAddが問題であり、このコードがコンパイルされない理由を正当化すると思いますが、に変更List<object> olするとコンパイルされます。IEnumerable<object> olこれにはAdd。がありません。(これはC#4で確認しました。)
Tim Goodman

その変更により、コンパイルされますがInvalidCastException、の実行時型olがまだであるため、がスローされますList<object>
ティムグッドマン

36

.NET 3.5を使用している場合は、Enumerable.Castメソッドを確認してください。これは拡張メソッドなので、リストから直接呼び出すことができます。

List<string> sl = new List<string>();
IEnumerable<object> ol;
ol = sl.Cast<object>();

それはあなたが求めたものとは正確には異なりますが、うまくいくはずです。

編集:Zoobaが指摘したように、ol.ToList()を呼び出してリストを取得できます


14

型パラメーターが異なるジェネリック型間でキャストすることはできません。特殊なジェネリック型は同じ継承ツリーの一部を形成しないため、無関係な型です。

これをNET3.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();

11

その理由は、のようなジェネリッククラスList<>は、ほとんどの目的で、外部では通常のクラスとして扱われるためです。たとえばList<string>()、コンパイラが言うときListString()(文字列を含む)。[技術者:これは、何が起こっているのかを非常にわかりやすく英語化したバージョンです]

その結果、コンパイラは、内部コレクションのアイテムをキャストすることによってListStringをListObjectに変換するほど賢くはないことは明らかです。

そのため、Convert()のようなIEnumerableの拡張メソッドを使用すると、コレクション内に格納されているアイテムの変換を簡単に提供できます。これは、あるものから別のものにキャストするのと同じくらい簡単です。


6

これは共分散と多くの関係があります。たとえば、ジェネリック型はパラメーターと見なされ、パラメーターがより具体的な型に適切に解決されない場合、操作は失敗します。そのような意味は、オブジェクトのようなより一般的なタイプにキャストすることは実際にはできないということです。また、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のすべての内容を問題なくコピーします。


5

はい、できます。.NET3.5から:

List<string> sl = new List<string>();
List<object> ol = sl.Cast<object>().ToList();



1

これは実際には、「ol」リストバリアントに奇妙な「オブジェクト」を入れようとしないList<object>ようにするためです(許可されているように見えます)-コードがクラッシュするためです(リストは実際にはList<string>文字列タイプのオブジェクトのみを受け入れるため) )。そのため、変数をより一般的な仕様にキャストすることはできません。

Javaではその逆で、ジェネリックはありません。代わりに、すべてが実行時のオブジェクトのリストであり、厳密に型指定されていると思われるリストに奇妙なオブジェクトを詰め込むことができます。「Reifiedgenerics」を検索して、Javaの問題に関する幅広い議論をご覧ください...


1

ジェネリックスでのこのような共分散はサポートされていませんが、実際には配列を使用してこれを行うことができます。

object[] a = new string[] {"spam", "eggs"};

C#の行いは、言って、入れてからあなたを防ぐためのチェック実行時intにはa


0

これは、コンテンツを暗黙的にキャストできるIListに対する.NET3.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;
}

(ズーバの例に基づく)


0

私は持っています:

private List<Leerling> Leerlingen = new List<Leerling>();

そして、私はそれを収集したデータで埋めるつもりでした。List<object> 最終的に私のために働いたのはこれでした:

Leerlingen = (List<Leerling>)_DeserialiseerLeerlingen._TeSerialiserenObjecten.Cast<Leerling>();

.Castそのタイプから取得したいIEnumerableタイプにそれを入力してIEnemuerableから、List<>必要なタイプにタイプキャストします。


0

うーん、以前のコメントのおかげで、私はそれを見つけるための2つの方法を見つけました。1つ目は、要素の文字列リストを取得し、それを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();

私は2番目の方法がもっと好きです。これがお役に立てば幸いです。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.