C #에서 List <string> 개체를 List <object> 변수에 저장할 수없는 이유


85

List 개체는 C #의 List 변수에 저장할 수 없으며 명시 적으로 캐스팅 할 수도 없습니다.

List<string> sl = new List<string>();
List<object> ol;
ol = sl;

암시 적으로 유형 System.Collections.Generic.List<string>을 다음으로 변환 할 수 없음System.Collections.Generic.List<object>

그리고...

List<string> sl = new List<string>();
List<object> ol;
ol = (List<object>)sl;

유형 System.Collections.Generic.List<string>을 다음으로 변환 할 수 없음System.Collections.Generic.List<object>

물론 문자열 목록에서 모든 것을 꺼내서 한 번에 하나씩 다시 넣는 방식으로 할 수 있지만 다소 복잡한 솔루션입니다.


5
이것은 C # 4.0에서 변경되므로 공분산 및 반공 분산을 조회 할 수 있습니다. 그런 일들을 안전한 방식으로 허용 할 것입니다.
John Leidegren


7
John> C # 4에서는이를 허용하지 않습니다. ol.Add (new object ()); 생각 해보세요.
Guillaume

여기에 Jon Skeet의 답변이 있습니다. stackoverflow.com/a/2033921/1070906
JCH2k

답변:


39

이런 식으로 생각해보십시오. 그런 캐스트를 한 다음 Foo 유형의 객체를 목록에 추가하면 문자열 목록이 더 이상 일관되지 않습니다. 첫 번째 참조를 반복하면 Foo 인스턴스에 도달하면 Foo를 문자열로 변환 할 수 없기 때문에 클래스 캐스트 예외가 발생합니다!

참고로 리버스 캐스트를 할 수 있는지 여부가 더 중요하다고 생각합니다.

List<object> ol = new List<object>();
List<string> sl;
sl = (List<string>)ol;

한동안 C #을 사용하지 않았기 때문에 그것이 합법적인지 모르겠지만 그런 종류의 캐스트가 실제로 (잠재적으로) 유용합니다. 이 경우 더 일반적인 클래스 (객체)에서 일반 클래스 (문자열)에서 확장되는 더 구체적인 클래스 (문자열)로 이동합니다. 이런 식으로 문자열 목록에 추가하는 경우 개체 목록을 위반하지 않습니다.

누구든지 그러한 캐스트가 C #에서 합법적인지 알고 있거나 테스트 할 수 있습니까?


4
나는 당신의 예가 같은 문제로 고통 받고 있다고 생각합니다. ol에 문자열이 아닌 것이 있으면 어떻게됩니까? 문제는 추가 / 삽입과 같은 List의 일부 메서드가 제대로 작동한다는 것입니다. 그러나 반복은 실제 문제가 될 수 있습니다.
Chris Ammerman

3
Eric Lippert는이 주제에 대한 일련의 블로그 게시물을 보유하고 있습니다. 공분산 및 반공 분산 제약 조건을 제네릭 메서드에 추가하는 것이 작동하지만 클래스 수준에서 원하는 방식으로 작동하지 않을 수있는 이유. is.gd/3kQc
Chris Ammerman

@ChrisAmmerman : ol문자열이 아닌 것이 있으면 런타임에 캐스트가 실패 할 것으로 예상합니다. 그러나 실제로 문제가 발생하는 부분은 캐스트가 성공한 다음ol 문자열이 아닌 무언가가 추가 된 경우 입니다. sl동일한 객체를 참조 하기 때문에 이제 List<string>비 문자열이 포함됩니다. 는 Add이 코드는 컴파일되지 않습니다 이유를 정당화 추측 문제이지만, 변경할 경우는 컴파일 List<object> olIEnumerable<object> ol을 가지고 있지 않는, Add. (C # 4에서 확인했습니다.)
Tim Goodman

이 변경으로 컴파일되지만 InvalidCastException의 런타임 유형 ol이 여전히이므로 List<object>.
Tim Goodman

36

.NET 3.5를 사용하는 경우 Enumerable.Cast 메서드를 살펴보십시오. 확장 메서드이므로 목록에서 직접 호출 할 수 있습니다.

List<string> sl = new List<string>();
IEnumerable<object> ol;
ol = sl.Cast<object>();

정확히 당신이 요청한 것은 아니지만 트릭을해야합니다.

편집 : Zooba에서 언급했듯이 ol.ToList ()를 호출하여 목록을 가져올 수 있습니다.


14

유형 매개 변수가 다른 일반 유형 간에는 캐스트 할 수 없습니다. 특수화 된 제네릭 유형은 동일한 상속 트리의 일부를 형성하지 않으므로 관련되지 않은 유형도 마찬가지입니다.

NET 3.5 이전을 수행하려면 :

List<string> sl = new List<string>();
// Add strings to sl

List<object> ol = new List<object>();

foreach(string s in sl)
{
    ol.Add((object)s);  // The cast is performed implicitly even if omitted
}

Linq 사용 :

var sl = new List<string>();
// Add strings to sl

var ol = new List<object>(sl.Cast<object>());

// OR
var ol = sl.Cast<object>().ToList();

// OR (note that the cast to object here is required)
var ol = sl.Select(s => (object)s).ToList();

11

그 이유는 같은 일반 클래스 List<>가 대부분의 경우 외부에서 일반 클래스로 취급되기 때문입니다. 예를 들어 List<string>()컴파일러가 ListString()(문자열 포함) 이라고 말할 때 . [기술 전문가 : 이건 무슨 일이 벌어지고 있는지 매우 평이한 영어 버전입니다.]

결과적으로 컴파일러는 내부 컬렉션의 항목을 캐스팅하여 ListString을 ListObject로 변환 할만큼 똑똑 할 수 없습니다.

그렇기 때문에 Convert ()와 같은 IEnumerable의 확장 메서드가있어 컬렉션 내에 저장된 항목에 대한 변환을 쉽게 제공 할 수 있습니다.이 메서드는 서로 캐스팅하는 것처럼 간단 할 수 있습니다.


6

이것은 공분산과 많은 관련이 있습니다. 예를 들어 제네릭 유형은 매개 변수로 간주되며 매개 변수가 더 구체적인 유형으로 제대로 해석되지 않으면 작업이 실패합니다. 이러한 의미는 객체와 같은보다 일반적인 유형으로 형변환 할 수 없다는 것입니다. Rex가 말했듯이 List 객체는 각 객체를 변환하지 않습니다.

대신 ff 코드를 시도해 볼 수 있습니다.

List<string> sl = new List<string>();
//populate sl
List<object> ol = new List<object>(sl);

또는:

List<object> ol = new List<object>();
ol.AddRange(sl);

ol은 (이론적으로) sl의 모든 내용을 문제없이 복사합니다.


5

예, .NET 3.5에서 다음을 수행 할 수 있습니다.

List<string> sl = new List<string>();
List<object> ol = sl.Cast<object>().ToList();



1

이는 실제로 "ol"목록 변형에 이상한 "객체"를 넣지 않도록하기위한 List<object>것입니다 ( 허용하는 것처럼 보임)-코드가 충돌 할 것이기 때문입니다 (목록이 실제로는 List<string>String 유형 객체 만 허용 하므로 ). 그렇기 때문에 변수를 더 일반적인 사양으로 캐스팅 할 수 없습니다.

Java에서는 그 반대입니다. 제네릭이없고 대신 모든 것이 런타임에 객체의 목록이며 엄격하게 유형이 지정된 목록에 이상한 객체를 채울 수 있습니다. "Reified generics"를 검색하여 Java의 문제에 대한 광범위한 토론을보십시오.


1

제네릭에 대한 이러한 공분산은 지원되지 않지만 실제로 배열을 사용하여이를 수행 할 수 있습니다.

object[] a = new string[] {"spam", "eggs"};

C #은 런타임 검사를 수행 int하여 a.


0

다음은 콘텐츠를 암시 적으로 캐스팅 할 수있는 모든 IList에 대한 또 다른 .NET 3.5 이전 솔루션입니다.

public IList<B> ConvertIList<D, B>(IList<D> list) where D : B
{
    List<B> newList = new List<B>();

    foreach (D item in list)
    {
        newList.Add(item);
    }

    return newList;
}

(Zooba의 예를 기반으로 함)


0

나는 :

private List<Leerling> Leerlingen = new List<Leerling>();

그리고 나는 List<object> 마침내 나를 위해 일한 것은 다음과 같은 것에서 수집 된 데이터로 채울 것입니다 .

Leerlingen = (List<Leerling>)_DeserialiseerLeerlingen._TeSerialiserenObjecten.Cast<Leerling>();

.Cast이 유형에 당신은 얻을 할 IEnumerable당시 캐스트, 해당 유형에서 IEnemuerable받는 사람이 List<>원하는.


0

음, 이전 의견 덕분에 두 가지 방법을 찾았습니다. 첫 번째는 요소의 문자열 목록을 가져온 다음 IEnumerable 개체 목록으로 캐스팅하는 것입니다.

IEnumerable<object> ob;
List<string> st = new List<string>();
ob = st.Cast<object>();

두 번째는 IEnumerable 객체 유형을 피하는 것입니다. 문자열을 객체 유형으로 캐스팅 한 다음 동일한 문장에서 "toList ()"함수를 사용하면됩니다.

List<string> st = new List<string>();
List<object> ob = st.Cast<object>().ToList();

나는 두 번째 방법을 더 좋아합니다. 이게 도움이 되길 바란다.


0
List<string> sl = new List<string>();
List<object> ol;
ol = new List<object>(sl);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.