web-dev-qa-db-ja.com

List <MyClass>からList <object>にキャストできないのはなぜですか?

QuoteHeaderタイプのオブジェクトのリストがあり、このリストをオブジェクトのリストとしてList<object>を受け入れることができるメソッドに渡したいと思います。

私のコード行は...

Tools.MyMethod((List<object>)MyListOfQuoteHeaders);

しかし、設計時に次のエラーが発生します...

Cannot convert type 'System.Collections.Generic.List<MyNameSpace.QuoteHeader>' 
to 'System.Collections.Generic.List<object>'

これを許可するには、クラスに何かする必要がありますか?すべてのクラスがオブジェクトから継承していると思ったので、なぜこれが機能しないのか理解できませんか?

23
Karl

これが合法でない理由は、安全ではないためです。それが合法だったとしましょう:

List<Giraffe> giraffes = new List<Giraffe>();
List<Animal> animals = giraffes; // this is not legal; suppose it were.
animals.Add(new Tiger());  // it is always legal to put a tiger in a list of animals

しかし、「動物」は実際にはキリンのリストです。キリンのリストにトラを入れることはできません。

C#では、残念ながら、これは参照型の配列では合法です。

Giraffe[] giraffes = new Giraffe[10];
Animal[] animals = giraffes; // legal! But dangerous because...
animals[0] = new Tiger(); // ...this fails at runtime!

C#4では、これはIEnumerableでは有効ですが、IListでは無効です:

List<Giraffe> giraffes = new List<Giraffe>();
IEnumerable<Animal> animals = giraffes; // Legal in C# 4
foreach(Animal animal in animals) { } // Every giraffe is an animal, so this is safe

IEnumerable<T>取り込む a Tのメソッドを公開しないため、安全です。

問題を解決するには、次のことができます。

  • 古いリストからオブジェクトの新しいリストを作成します。
  • メソッドにList<object>ではなくobject []を取得させ、安全でない配列共分散を使用します。
  • メソッドをジェネリックにするので、List<T>が必要です
  • メソッドにIEnumerableを使用させる
  • メソッドにIEnumerable<object>を取り、C#4を使用させます。
41
Eric Lippert

キャストすることはできませんList<OneType>からList<OtherType>実際には、キャストするリストのインスタンスであり、リスト自体でもあります。

これを可能にする拡張メソッドがあります( MSDNリファレンス ):

IEnumerable<Object> myNewEnumerable = myEnumerable.Cast<Object>();

このメソッドは、あるタイプのリストの各インスタンスを別のタイプにキャストし、それらを新しい列挙型に追加しようとします。インスタンスをキャストできない場合は、例外がスローされます。

システムに関する限り、リストの2つのタイプは異なるタイプであるため、次のようになります。

A objectOfTypeA;
B objectOfTypeB = (B) objectofTypeA;

キャストを実行できるようにするには、使用可能なタイプ間で暗黙的または明示的な変換が必要になりますが、そうではありません(提供した場合を除き、実行できる可能性があります)。

List<object>は常に別のリストに任意の型を保持できますが、それらの用語で考えると、なぜそうでないのかがわかります。

もっと技術的に有能な答えがあると確信していますが、それがその要点だと思います。

共変性と反変性に関するEric Lippertのシリーズ を読むことに興味があるかもしれません。これはあなたに役立つかもしれません。

この質問 も役立つかもしれません

5
Sam Holder
List<MyClass> x = someList.Select(f => (MyClass)f).ToList();
4
lz1

リストは相互に継承するタイプであるか、そうでなければあるタイプから別のタイプにキャストできることを意味していると思います-その場合は、これを試してください:

Tools.MyMethod(MyListOfQuoteHeaders.Cast<OtherType>());
1
Nathan

多くの回答をありがとう。

私がやりたかったことと、解決策として思いついたことを説明します。

任意のタイプのオブジェクトのリストを渡して、そのリストをXMLに出力することで呼び出すことができるメソッドが必要でした。また、このメソッドに渡されるのは、XMLファイルが保存される場所を指すシステムファイル構造パスの場所となる文字列です。クラスとタイプの数は増え続けているので、クラスのタイプごとに対応するために複数のメソッドを作成することは避けたかったのです。これを正しい方法で行ったかどうかはわかりませんが、これは私の問題に対する軽量の解決策であり、機能します。何か問題があったり、コメントがあればお気軽に...

だから...私の方法は今このようになっています...

    public static Enums.Status WriteListToXML<T>(string outputPath, List<T> inboundList)
    {
        try
        {
            XmlSerializer xmlSerializer = new XmlSerializer(inboundList.GetType());
            using (StreamWriter streamWriter = System.IO.File.CreateText(outputPath))
            {
                xmlSerializer.Serialize(streamWriter, inboundList);
            }
            return Enums.Status.Success;
        }
        catch (Exception ex)
        {
            return Enums.Status.Failure;
        }
    }

...そして私のコーリングラインは...

WriteListToXML<QuoteHeader>(@"C:\XMLDocuments\output\QuoteHeaders.xml", quoteHeadersList);

私が言ったように、それは最もきちんとした解決策ではないかもしれませんが、私のシナリオではうまく機能します。

0
Karl