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 <Y>에 추가하면 실제로는 List <X>가 될 것입니다.
Richard Friend

답변:


497

경우 X정말 캐스팅 할 수 있습니다 Y당신은 사용할 수 있어야합니다

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

알아 두어야 할 사항 (해설자에게 H / T!)


12
또 다른 금 배지를 갖습니다. 이것은 매우 유용했습니다.
ouflak

6
컴파일러가 이러한 확장 메서드를 인식하도록하려면 다음 줄을 포함해야합니다. using System.Linq;
hypehuman

8
또한 목록의 각 항목을 캐스트하더라도 목록 자체는 캐스트되지 않습니다. 오히려 원하는 유형으로 새 목록이 작성됩니다.
hypehuman

4
또한이 Cast<T>메소드는 맞춤 전환 연산자를 지원하지 않습니다. 이유는 암시 적 캐스트 운영자와의 LINQ 캐스트 도우미하지 작동 하는가 .
clD

명시적인 연산자 메소드 (framework 4.0)를 가진 객체에는 작동하지 않습니다
Adrian

100

직접 캐스팅 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

2
나는 지금까지 ive 가이 답변을 +1하지 않았다고 믿을 수 없다. 위의 것보다 훨씬 낫습니다.
Jamiec 2016 년

6
@Jamiec 나는 "아니오, 불가능합니다"로 시작하기 때문에 +1하지 않았습니다.이 질문에 대한 답을 찾는 많은 사람들이 묻었습니다. 기술적으로 그는 OP의 질문에 더 철저하게 대답했습니다.
Dan Bechard

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

안녕하세요, 방금 "type 필터링에 foreach () 사용"예제를 사용해 보았습니다. var list = new List <object> () {1, "a", 2, "b", 3, "c", 4, " d "}; foreach (int i in list) Console.WriteLine (i); 실행하면 "지정된 캐스트가 유효하지 않습니다."라는 메시지가 나타납니다. 뭔가 빠졌습니까? 나는 foreach가 이런 식으로 효과가 있다고 생각하지 않았기 때문에 시도하고 있습니다.
브렌트 리튼 하우스

또한 참조 대 값 유형이 아닙니다. 방금 'Thing'의 기본 클래스와 'Person'및 'Animal'의 두 가지 파생 클래스로 시도했습니다. 같은 작업을 수행하면 "Animal"형식의 개체를 'Person'형식으로 캐스팅 할 수 없습니다. " 따라서 각 요소를 반복적으로 반복합니다. 목록에서 OfType을 수행하면 작동합니다. 컴파일러가 최적화하지 않는 한 ForEach는 이것을 확인해야한다면 실제로 느릴 것입니다.
브렌트 리튼 하우스

고마워 브렌트-나는 거기에서 벗어났다. foreach필터링하지는 않지만 반복 변수로 더 파생 된 유형을 사용하면 컴파일러가 캐스트를 시도하도록 강제 할 수 있으며, 이는 준수하지 않는 첫 번째 요소에서 실패합니다.
StuartLC

7

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


3

이것은이 질문에 확실히 대답은 아니지만 일부 유용 할 수 있습니다 : @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

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


1
큰 소스 목록이 있으면 처음에는 성능 저하가 없기 때문에 이것을 좋아합니다. 대신 수신자가 처리하는 각 항목에 대해 눈에 띄지 않는 작은 캐스트가 있습니다. 또한 거대한 메모리가 구축되지 않습니다. 스트림 처리에 적합합니다.
Johan Franzén

-2
dynamic data = List<x> val;  
List<y> val2 = ((IEnumerable)data).Cast<y>().ToList();
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.