다음과 같이 한 번에 하나씩 한 유형에서 다른 유형으로 항목 목록을 캐스팅 할 수 있다는 것을 알고 있습니다 (캐스팅을 수행하는 공개 정적 명시 적 연산자 메소드가있는 경우).
List<Y> ListOfY = new List<Y>();
foreach(X x in ListOfX)
ListOfY.Add((Y)x);
그러나 한 번에 전체 목록을 전송할 수 없습니까? 예를 들어
ListOfY = (List<Y>)ListOfX;
다음과 같이 한 번에 하나씩 한 유형에서 다른 유형으로 항목 목록을 캐스팅 할 수 있다는 것을 알고 있습니다 (캐스팅을 수행하는 공개 정적 명시 적 연산자 메소드가있는 경우).
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;
이 확장 방법을 얻으려면 포함시켜야합니다List<Y>
호출하면 새로운 내용이 생성됩니다 ToList()
.Cast<T>
메소드는 맞춤 전환 연산자를 지원하지 않습니다. 이유는 암시 적 캐스트 운영자와의 LINQ 캐스트 도우미하지 작동 하는가 .
직접 캐스팅 var ListOfY = (List<Y>)ListOfX
이 필요하기 때문에 가능하지 않다 공동 / contravariance 의 List<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>
공분산을 지원 하기 때문 입니다. 이는 컬렉션에서 요소를 추가하거나 제거하는 메서드를 지원하지 않으므로 컬렉션을 변경할 수 없음 T
을 IEnumerable
의미합니다. out
의 선언 에서 키워드를 참고하십시오 IEnumerable<T>
.
public interface IEnumerable<out T> : IEnumerable
( 변경 불가능한 반복자 및 콜렉션이 지원할 수있는 것과 같은 변경 가능한 콜렉션이 지원할 수없는 이유에 대한 추가 설명 이 있습니다.)List
covariance
로 캐스팅 .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
foreach
필터링하지는 않지만 반복 변수로 더 파생 된 유형을 사용하면 컴파일러가 캐스트를 시도하도록 강제 할 수 있으며, 이는 준수하지 않는 첫 번째 요소에서 실패합니다.
이것은이 질문에 확실히 대답은 아니지만 일부 유용 할 수 있습니다 : @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
가장 큰 장점은 메모리에 새 목록을 만들지 않는다는 것입니다.