次のように、アイテムのリストをあるタイプから別のタイプにキャストできることを知っています(オブジェクトにキャストを行うためのパブリック静的明示的演算子メソッドがある場合)。
List<Y> ListOfY = new List<Y>();
foreach(X x in ListOfX)
ListOfY.Add((Y)x);
しかし、リスト全体を一度にキャストすることはできませんか?例えば、
ListOfY = (List<Y>)ListOfX;
X
を実際にY
にキャストできる場合、使用できるはずです。
List<Y> listOfY = listOfX.Cast<Y>().ToList();
知っておくべきいくつかのこと(コメンターへのH/T!)
using System.Linq;
を含める必要がありますToList()
の呼び出しにより、新しいList<Y>
が作成されます。var ListOfY = (List<Y>)ListOfX
の直接キャストは、List<T>
タイプの co/contravariance を必要とするため不可能であり、すべての場合に保証されるわけではありません。このキャストの問題の解決策をご覧ください。
このようなコードを書くことができるのは普通のように思えますが:
List<Animal> animals = (List<Animal>) mammalList;
すべての哺乳類が動物になることを保証できるため、これは明らかに間違いです。
List<Mammal> mammals = (List<Mammal>) animalList;
すべての動物が哺乳類ではないからです。
ただし、C#3以上を使用すると、次を使用できます。
IEnumerable<Animal> animals = mammalList.Cast<Animal>();
これにより、キャストが少し楽になります。これは、リスト内の各Mammal
を明示的なキャストを使用してAnimal
にキャストするため、コードを1つずつ追加するのと構文的に同等であり、キャストが成功しないと失敗します。
キャスト/変換プロセスをさらに制御したい場合は、List<T>
クラスのConvertAll
メソッドを使用できます。これは、提供された式を使用してアイテムを変換できます。 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
Swekoのポイントに追加するには:
キャストした理由
var listOfX = new List<X>();
ListOf<Y> ys = (List<Y>)listOfX; // Compile error: Cannot implicitly cast X to Y
List<T>
はType Tで不変であり、したがってX
name__がY
name__から派生するかどうかは関係ないため、これは不可能です。これは、List<T>
が次のように定義されているためです。
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T> ... // Other interfaces
(この宣言では、ここにT
name__と入力すると、追加の分散修飾子がないことに注意してください)
ただし、設計で可変コレクションが必要でない場合は、多くの不変コレクションのアップキャストが可能です。ただし、Giraffe
name__はAnimal
name__から派生します。
IEnumerable<Animal> animals = giraffes;
これは、IEnumerable<T>
がT
name__の共分散をサポートしているためです。これは、IEnumerable
name__がコレクションの要素を追加または削除するメソッドをサポートしていないため、コレクションを変更できないことを意味するため、意味があります。 IEnumerable<T>
の宣言のout
name__キーワードに注意してください。
public interface IEnumerable<out T> : IEnumerable
( 詳細説明List
name__のような可変コレクションがcovariance
name__をサポートできないのに対し、不変のイテレーターとコレクションはサポートできない理由のため。)
.Cast<T>()
を使用したキャスト
他の人が言及したように、 .Cast<T>()
はコレクションに適用してTにキャストされた要素の新しいコレクションを投影できますが、1つ以上の要素へのキャストが不可能な場合はInvalidCastException
name__をスローします(これはOPのforeach
name__ループで明示的なキャストを行うのと同じ動作になります)。
OfType<T>()
を使用したフィルタリングとキャスト
入力リストに異なる互換性のないタイプの要素が含まれる場合、.OfType<T>()
の代わりに .Cast<T>()
を使用することで、潜在的なInvalidCastException
name__を回避できます。 (.OfType<>()
は、変換を試みる前に、要素をターゲット型に変換できるかどうかを確認し、互換性のない型を除外します。)
foreach
また、OPが代わりにこれを書いた場合:(foreach
name__の明示的なY y
に注意してください)
List<Y> ListOfY = new List<Y>();
foreach(Y y in ListOfX)
{
ListOfY.Add(y);
}
キャストも試みられます。ただし、キャストが不可能な場合は、InvalidCastException
name__が生成されます。
例
たとえば、単純な(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:暗黙のキャスト演算子
動的でない場合、ユーザー定義の変換演算子は compile-time *でのみ使用されるため、たとえばZebraとElephantの間の変換演算子が使用可能になったとしても、上記の変換アプローチの実行時の動作は無効になります。 tを変更します。
ZebraをElephantに変換する変換演算子を追加する場合:
public class Zebra : Animal
{
public Zebra(string name) : base(name) { }
public static implicit operator Elephant(Zebra z)
{
return new Elephant(z.Name);
}
}
代わりに、上記の変換演算子が与えられると、コンパイラーは、Zebraを同種のゾウのコレクションに変換できるようになると、以下の配列の型をAnimal[]
からElephant[]
に変更できます。
var compilerInferredAnimals = new []
{
new Zebra("Zed"),
new Elephant("Ellie")
};
実行時に暗黙的な変換演算子を使用する
*エリックが述べたように、実行時に変換演算子にアクセスするには、dynamic
name__を使用します。
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
List<Y>.ConvertAll<T>([Converter from Y to T]);
を使用できます