web-dev-qa-db-ja.com

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

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

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

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

しかし、リスト全体を一度にキャストすることはできませんか?例えば、

ListOfY = (List<Y>)ListOfX;
205
Jimbo

Xを実際にYにキャストできる場合、使用できるはずです。

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

知っておくべきいくつかのこと(コメンターへのH/T!)

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

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
88
SWeko

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

キャストした理由

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

List<T>Type Tで不変であり、したがってXname__がYname__から派生するかどうかは関係ないため、これは不可能です。これは、List<T>が次のように定義されているためです。

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

(この宣言では、ここにTname__と入力すると、追加の分散修飾子がないことに注意してください)

ただし、設計で可変コレクションが必要でない場合は、多くの不変コレクションのアップキャストが可能です。ただし、Giraffename__はAnimalname__から派生します。

IEnumerable<Animal> animals = giraffes;

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

public interface IEnumerable<out T> : IEnumerable

詳細説明Listname__のような可変コレクションがcovariancename__をサポートできないのに対し、不変のイテレーターとコレクションはサポートできない理由のため。)

.Cast<T>()を使用したキャスト

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

OfType<T>()を使用したフィルタリングとキャスト

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

foreach

また、OPが代わりにこれを書いた場合:(foreachname__の明示的なY yに注意してください)

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

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

キャストも試みられます。ただし、キャストが不可能な場合は、InvalidCastExceptionname__が生成されます。

たとえば、単純な(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")
};

実行時に暗黙的な変換演算子を使用する

*エリックが述べたように、実行時に変換演算子にアクセスするには、dynamicname__を使用します。

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
9
StuartLC

List<Y>.ConvertAll<T>([Converter from Y to T]);を使用できます

7
Andrey