List <X>からList <Y>にキャストするための短い構文?


237

次のように、アイテムのリストをあるタイプから別のタイプにキャストすることは可能です(オブジェクトにキャストを実行するための静的で明示的な演算子メソッドがある場合)。

List<Y> ListOfY = new List<Y>();

foreach(X x in ListOfX)
    ListOfY.Add((Y)x);

しかし、リスト全体を一度にキャストすることは不可能ですか?例えば、

ListOfY = (List<Y>)ListOfX;

@Oded:私はそれをもう少し明確にしようとしました。心配しないでください、あなたはそうではありません、私は理解しています:)
BoltClock

1
XがYから派生し、ZがYから派生すると仮定して、Zを実際にList <X>であるList <Y>に追加するとどうなるか考えてください。
リチャードフレンド

回答:


497

場合はX、実際にキャストすることができYますが、使用することができるはずです

List<Y> listOfY = listOfX.Cast<Y>().ToList();

注意点(コメント投稿者へのH / T!)

  • using System.Linq;この拡張メソッドを取得するには含める必要があります
  • これにより、リスト自体ではなく、リスト内の各アイテムがキャストされます。新しいList<Y>はへの呼び出しによって作成されますToList()
  • このメソッドは、カスタム変換演算子をサポートしていません。(http://stackoverflow.com/questions/14523530/why-does-the-linq-cast-helper-not-work-with-the-implicit-cast-operatorを参照してください
  • このメソッドは、明示的な演算子メソッドを持つオブジェクト(フレームワーク4.0)では機能しません

12
別の金のバッジを持っています。これは非常に役に立ちました。
ouflak 2015

6
コンパイラーにこれらの拡張メソッドを認識させるには、次の行を含める必要があります。using System.Linq;
hypehuman 2015

8
また、これによりリスト内の各項目がキャストされますが、リスト自体はキャストされないことに注意してください。むしろ、希望するタイプで新しいリストが作成されます。
hypehuman 2015

4
また、Cast<T>メソッドはカスタム変換演算子をサポートしていないことにも注意してください。Linqキャストヘルパーが暗黙的キャストオペレーターで機能しないのはなぜですか
clD 2016

明示的な演算子メソッド(フレームワーク4.0)を持つオブジェクトでは機能しません
Adrian

100

直接キャストvar ListOfY = (List<Y>)ListOfXは、型の共分散を必要とするためList<T>不可能であり、すべてのケースで保証されるわけではありません。このキャストの問題の解決策については、以下をお読みください。

次のようなコードを書くことができるのは普通のようですが:

List<Animal> animals = (List<Animal>) mammalList;

すべての哺乳動物が動物になることを保証できるため、これは明らかに間違いです。

List<Mammal> mammals = (List<Mammal>) animalList;

すべての動物が哺乳類であるわけではないので。

ただし、C#3以降を使用すると、

IEnumerable<Animal> animals = mammalList.Cast<Animal>();

キャストが少し楽になります。これは、1つずつ追加するコードと構文的に同等です。明示的なキャストを使用Mammalしてリスト内のそれぞれをにキャストしAnimal、キャストが成功しない場合は失敗します。

キャスト/変換プロセスをより詳細に制御したい場合は、提供された式を使用してアイテムを変換できるクラスのConvertAllメソッドを使用できますList<T>。のList代わりにを返すという利点があるIEnumerableため、.ToList()必要ありません。

List<object> o = new List<object>();
o.Add("one");
o.Add("two");
o.Add(3);

IEnumerable<string> s1 = o.Cast<string>(); //fails on the 3rd item
List<string> s2 = o.ConvertAll(x => x.ToString()); //succeeds

2
今までこの答えを+1したことがないとは信じられません。上記のものよりもはるかに良いです。
Jamiec 2014年

6
@Jamiec私が+1をしなかったのは、「いいえ、不可能です」で始まっているためですが、この質問を見つけた多くの人が探している答えを埋めています。技術的には、彼はOPの質問にもっと完全に答えました。
Dan Bechard、2014

13

Swekoのポイントに追加するには:

キャストの理由

var listOfX = new List<X>();
ListOf<Y> ys = (List<Y>)listOfX; // Compile error: Cannot implicitly cast X to Y

ことができないためでList<T>あるタイプTに不変ので、それはかどうかは関係ありませんXから派生Y)は-これがあるためであるList<T>と定義されています。

public class List<T> : IList<T>, ICollection<T>, IEnumerable<T> ... // Other interfaces

(この宣言では、Tここにタイプを追加する分散修飾子がないことに注意してください)

ただし、設計で可変コレクションが不要な場合、たとえば次のようにGiraffe派生する場合、不変コレクションの多くでアップキャストが可能Animalです。

IEnumerable<Animal> animals = giraffes;

これはでIEnumerable<T>共分散をサポートするためです。これは、コレクションから要素を追加または削除するメソッドをサポートしていないため、コレクションを変更できないことTIEnumerable意味します。outの宣言のキーワードに注意してくださいIEnumerable<T>

public interface IEnumerable<out T> : IEnumerable

(のような変更可能なコレクションがをサポートできないのに対し、不変のイテレータとコレクションはサポートできる理由の詳細な説明を次に示します。)Listcovariance

キャスティング .Cast<T>()

他の人が述べたよう.Cast<T>()に、コレクションに適用して、Tにキャストされた要素の新しいコレクションを投影できますがInvalidCastException、1つ以上の要素へのキャストが不可能な場合は、これをスローします(これは、明示的に行うのと同じ動作です) OPのforeachループでキャスト)。

でのフィルタリングとキャスト OfType<T>()

入力リストに異なる互換性のないタイプの要素が含まれている場合は、の代わりにInvalidCastExceptionを使用することでこの可能性を回避できます。(変換を試みる前に、要素がターゲットタイプに変換できるかどうかを確認し、互換性のないタイプを除外します。).OfType<T>().Cast<T>().OfType<>()

foreach

OPがこれを代わりに作成した場合も注意してください:(で明示的Y yに注意してくださいforeach

List<Y> ListOfY = new List<Y>();

foreach(Y y in ListOfX)
{
    ListOfY.Add(y);
}

キャスティングも試みられること。ただし、キャストが不可能な場合は、InvalidCastException結果になります。

たとえば、単純な(C#6)クラス階層があるとします。

public abstract class Animal
{
    public string Name { get;  }
    protected Animal(string name) { Name = name; }
}

public class Elephant :  Animal
{
    public Elephant(string name) : base(name){}
}

public class Zebra : Animal
{
    public Zebra(string name)  : base(name) { }
}

混合タイプのコレクションを操作する場合:

var mixedAnimals = new Animal[]
{
    new Zebra("Zed"),
    new Elephant("Ellie")
};

foreach(Animal animal in mixedAnimals)
{
     // Fails for Zed - `InvalidCastException - cannot cast from Zebra to Elephant`
     castedAnimals.Add((Elephant)animal);
}

var castedAnimals = mixedAnimals.Cast<Elephant>()
    // Also fails for Zed with `InvalidCastException
    .ToList();

一方:

var castedAnimals = mixedAnimals.OfType<Elephant>()
    .ToList();
// Ellie

象のみを除外します。つまり、シマウマは排除されます。

Re:暗黙のキャスト演算子

動的ではなく、ユーザー定義の変換演算子はコンパイル時にのみ使用されるため *たとえば、ZebraとElephantの間の変換演算子が使用可能になったとしても、変換に対するアプローチの上記の実行時の動作は変わりません。

ZebraをElephantに変換する変換演算子を追加すると、次のようになります。

public class Zebra : Animal
{
    public Zebra(string name) : base(name) { }
    public static implicit operator Elephant(Zebra z)
    {
        return new Elephant(z.Name);
    }
}

その代わりに、上記変換演算子が与えられると、コンパイラは、アレイの下方からのタイプに変更することができるようになるAnimal[]までのElephant[]シマウマは今象の均一な集まりに変換することができることを考えると、。

var compilerInferredAnimals = new []
{
    new Zebra("Zed"),
    new Elephant("Ellie")
};

実行時の暗黙の変換演算子の使用

*エリックが述べたように、変換演算子は実行時に次の方法でアクセスできますdynamic

var mixedAnimals = new Animal[] // i.e. Polymorphic collection
{
    new Zebra("Zed"),
    new Elephant("Ellie")
};

foreach (dynamic animal in mixedAnimals)
{
    castedAnimals.Add(animal);
}
// Returns Zed, Ellie

ねえ、私はちょうど「タイプフィルタリングにforeach()を使用する」の例を使用して試しました:var list = new List <object>(){1、 "a"、2、 "b"、3、 "c"、4、 " d "}; foreach(リストのint i)Console.WriteLine(i); 実行すると、「指定されたキャストは無効です」と表示されます。何か不足していますか?foreachがこのように機能するとは思わなかったので、私はそれを試しました。
ブレントリッテンハウス、

また、これは参照型と値型ではありません。「Thing」の基本クラスと2つの派生クラス「Person」と「Animal」で試してみました。同じことをすると、「タイプが「動物」のオブジェクトをタイプが「人」にキャストできません。」だから、それは間違いなく各要素を反復しています。リストでOfTypeを実行すると、うまくいきます。コンパイラが最適化しない限り、これをチェックする必要がある場合、ForEachはおそらく本当に遅くなります。
ブレントリッテンハウス

ありがとうブレント-私はコースから外れました。foreachはフィルタリングしませんが、反復変数としてより派生型を使用すると、コンパイラーにキャストの試行を強制します。これは、準拠していない最初の要素で失敗します。
StuartLC 2018年


3

これはこの質問に対する答えではありませんが、一部の人にとっては役立つかもしれません。@ SWekoが言ったように、共分散と反変のおかげList<X>List<Y>、にキャストすることはできませんが、にキャストするList<X>ことができIEnumerable<Y>、暗黙のキャストでも可能です。

例:

List<Y> ListOfY = new List<Y>();
List<X> ListOfX = (List<X>)ListOfY; // Compile error

だが

List<Y> ListOfY = new List<Y>();
IEnumerable<X> EnumerableOfX = ListOfY;  // No issue

大きな利点は、メモリに新しいリストを作成しないことです。


1
ソースリストが大きい場合、最初はパフォーマンスに影響がないため、これが好きです。代わりに、受信者によって処理されている各エントリに対して、目立たない小さなキャストがあります。また、巨大なメモリも蓄積されません。ストリームの処理に最適です。
JohanFranzén19年

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