C#에서 List<string>List<object>에 저장할 수 없는 이유는 무엇인가요?

C#은 강력한 타입 지정과 객체 지향 기능을 제공하는 강력한 프로그래밍 언어입니다. 개발자들이 흔히 겪는 문제 중 하나는 제네릭과 관련된 것으로, 특히 리스트를 다룰 때 발생합니다. 자주 묻는 질문은: List<string> 객체를 List<object> 변수에 저장할 수 없는가? 이 주제를 깊이 파고들어 이 동작의 근본 원리와 의미를 이해해 보겠습니다.

리스트와 제네릭 이해하기

C#에서 List<T>는 지정된 타입 T의 여러 항목을 저장할 수 있는 제네릭 컬렉션입니다. 예를 들어, List<string>은 문자열만 저장할 수 있고, List<object>는 모든 객체 타입을 저장할 수 있습니다. 왜냐하면 object는 C#의 모든 타입의 기반 클래스이기 때문입니다. 이는 C#에서 타입 안전성의 중요한 측면으로 이어집니다.

핵심 문제

List<string>List<object>에 저장하려고 할 때 다음과 같은 오류가 발생합니다:

Cannot implicitly convert type 'System.Collections.Generic.List<string>' to 'System.Collections.Generic.List<object>'

이는 C#이 타입 매개변수가 다른 제네릭 타입끼리 암시적으로 변환하거나 캐스팅하는 것을 허용하지 않기 때문입니다. 간단히 말하면:

  • List<string>List<object>는 서로 다른 타입입니다.
  • C#은 코드의 안전성을 보장하기 위해 이 타입 구분을 강제합니다.

이것이 중요한 이유는 무엇인가요?

이러한 타입 안전성이 왜 중요한지 살펴보겠습니다. List<string>List<object>로 캐스팅할 수 있다면, 다른 타입의 객체(예: Foo)를 그 리스트에 추가할 수 있다고 상상해 보십시오. 리스트는 문자열과 이 Foo 객체를 모두 포함하게 되어 일관성이 깨집니다. 나중에 이 리스트에서 문자열로 요소에 접근하려고 하면, Foo 인스턴스를 만났을 때 런타임 오류, 즉 ClassCastException이 발생하게 됩니다.

예제 시나리오

다음 코드를 고려해 보세요:

List<string> sl = new List<string>();
List<object> ol;
ol = sl; // 이 줄에서 오류 발생

이 코드가 컴파일된다면, 다음과 같은 요소를 추가할 수 있게 됩니다:

ol.Add(new Foo()); // 이제 문자열 리스트에 Foo가 추가됨

그 후 sl을 반복하면서 다시 문자열로 캐스팅하려 하면 실패하게 됩니다. 왜냐하면 모든 요소가 더 이상 문자열이 아니기 때문입니다.

명시적 캐스팅 시도

명시적 캐스트를 시도해 List<string>List<object>로 변환해 보려 할 수도 있습니다:

sl = (List<object>)ol; // 이것도 오류 발생

여기에서도 동일한 원칙이 적용됩니다. C#은 이 캐스트를 허용하지 않습니다. stringobject에서 파생되지만, List<T>는 동일한 공변성을 보이지 않기 때문입니다. 공변성은 IEnumerable<Derived>IEnumerable<Base>로 처리하는 것을 허용하지만, 리스트와 같은 컬렉션에는 적용되지 않습니다.

반대 방향으로 캐스팅이 가능한가요?

List<object>에서 List<string>으로 캐스팅하는 것이 가능한지 궁금할 수 있습니다. 예를 들어:

List<object> ol = new List<object>();
List<string> sl;
sl = (List<string>)ol; // 이것이 합법적인가요?

이 특정 연산도 간단하지 않습니다. 이 캐스트는 더 일반적인 유형에서 더 특정한 유형으로 이동하려고 시도하지만 위험하다고 간주됩니다. 만약 ol이 문자열이 아닌 요소를 포함하고 있다면, 잘못된 캐스트 예외가 발생할 것입니다.

대체 솔루션

이러한 타입 안전 기능이 제한적으로 보일 수 있지만, C#에서 컬렉션의 무결성을 유지하는 데 중요합니다. 다양한 타입을 저장해야 한다면 다음 옵션을 고려해 보세요:

  • 다형성 사용: 공통 기반 타입의 객체를 저장하고 이들과 작업할 방법을 구현합니다.
  • 객체 리스트 사용: 타입을 혼합해야 한다면 List<object> 사용이 적합할 수 있지만, 검색 및 캐스팅 시 주의가 필요합니다.

결론

요약하자면, C#의 강력한 타입 안전성 메커니즘 때문에 List<string>List<object>에 저장할 수 없습니다. 제네릭의 이 측면을 이해하는 것은 런타임 오류를 방지하고 애플리케이션 내 데이터 무결성을 유지하는 데 도움이 됩니다. 이러한 원칙을 준수하면 더 견고한 C# 애플리케이션을 개발할 수 있습니다.

이 주제에 대해 추가 질문이나 통찰력이 있으시면 생각을 공유해 주세요!