[C#] List <X>에서 List <Y>로 캐스트하는 더 짧은 구문?

다음과 같이 한 번에 하나씩 한 유형에서 다른 유형으로 항목 목록을 캐스팅 할 수 있다는 것을 알고 있습니다 (캐스팅을 수행하는 공개 정적 명시 적 연산자 메소드가있는 경우).

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!)


답변

직접 캐스팅 var ListOfY = (List<Y>)ListOfX이 필요하기 때문에 가능하지 않다 공동 / contravarianceList<T>유형을, 그 다만 모든 경우에 보장 할 수 없습니다. 이 캐스팅 문제에 대한 솔루션을 보려면 계속 읽으십시오.

다음과 같은 코드를 작성할 수있는 것이 정상인 것 같습니다.

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

우리는 모든 포유류가 동물이 될 것이라고 보장 할 수 있기 때문에 이것은 분명히 실수입니다.

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

모든 동물이 포유류가 아니기 때문입니다.

그러나 C # 3 이상을 사용하면

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

캐스팅이 약간 쉬워집니다. 이는 명시적인 캐스트를 사용 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


답변

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하나 이상의 요소에 대한 캐스트가 불가능한 경우 (명시적인 작업과 동일한 동작) OP의 foreach루프 에서 캐스팅 ).

필터링 및 캐스팅 OfType<T>()

입력 목록에 서로 호환되지 않는 다른 유형의 요소가 포함 된 경우 대신을 InvalidCastException사용하여 가능성 을 피할 수 있습니다 . ( 요소가 변환을하기 전에, 상기 타겟 형태로 변환하고, 필터 유형을 비교할 수없는 아웃 될 수 있는지 여부를 확인한다.).OfType<T>().Cast<T>().OfType<>()

각각

(노트 : 또한 영업 이익은 대신을 쓴 경우주의 명시 적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

코끼리 만 걸러냅니다. 즉 얼룩말이 제거됩니다.

다시 : 암시 적 캐스트 연산자

동적이 없으면 사용자 정의 변환 연산자는 컴파일 타임 에만 사용 되므로 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")
};

런타임에 암시 적 변환 연산자 사용

* Eric이 언급했듯이 변환 연산자는 다음을 사용하여 런타임에 액세스 할 수 있습니다 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


답변

당신이 사용할 수있는 List<Y>.ConvertAll<T>([Converter from Y to T]);


답변

이것은이 질문에 확실히 대답은 아니지만 일부 유용 할 수 있습니다 : @SWeko 말했듯이, 공분산 및 contravariance 덕분 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

가장 큰 장점은 메모리에 새 목록을 만들지 않는다는 것입니다.


답변

dynamic data = List<x> val;
List<y> val2 = ((IEnumerable)data).Cast<y>().ToList();


답변