문제

다음 예를보십시오 (부분적으로 찍은 MSDN 블로그):

class Animal { }
class Giraffe : Animal { }

static void Main(string[] args)
{
    // Array assignment works, but...
    Animal[] animals = new Giraffe[10]; 

    // implicit...
    List<Animal> animalsList = new List<Giraffe>();

    // ...and explicit casting fails
    List<Animal> animalsList2 = (List<Animal>) new List<Giraffe>();
}

이것이 공분산 문제입니까? 이것은 향후 C# 릴리스에서 지원되며 영리한 해결 방법이 있습니까 (.NET 2.0 만 사용)가 있습니까?

도움이 되었습니까?

해결책

글쎄, 이것은 C# 4에서 확실히 뒷받침되지 않을 것입니다. 근본적인 문제가 있습니다.

List<Giraffe> giraffes = new List<Giraffe>();
giraffes.Add(new Giraffe());
List<Animal> animals = giraffes;
animals.Add(new Lion()); // Aargh!

기린을 안전하게 유지하십시오. 안전하지 않은 분산을 거절하십시오.

배열 버전은 배열이기 때문에 작동합니다 하다 실행 시간 확인을 통해 참조 유형 분산을 지원합니다. 제네릭의 요점은 제공하는 것입니다 컴파일 타임 타입 안전.

C# 4에는 지원이 있습니다 안전한 일반적인 분산이지만 인터페이스 및 대표단에만 해당됩니다. 그래서 당신은 할 수 있습니다 :

Func<string> stringFactory = () => "always return this string";
Func<object> objectFactory = stringFactory; // Safe, allowed in C# 4

Func<out T> ~이다 공분산 안에 T 왜냐하면 T 출력 위치에만 사용됩니다. 비교하십시오 Action<in T> 이는 비밀입니다 T 왜냐하면 T 입력 위치에만 사용되므로 안전합니다.

Action<object> objectAction = x => Console.WriteLine(x.GetHashCode());
Action<string> stringAction = objectAction; // Safe, allowed in C# 4

IEnumerable<out T> 다른 사람들이 지적한대로 C# 4에서 이것을 올바르게 만드는 것도 공변량입니다.

IEnumerable<Animal> animals = new List<Giraffe>();
// Can't add a Lion to animals, as `IEnumerable<out T>` is a read-only interface.

C# 2의 상황 에서이 문제를 해결하는 측면에서 하나 목록, 아니면 새 목록을 만드는 것이 행복 하시겠습니까? 그것이 받아 들일 수 있다면 List<T>.ConvertAll 당신의 친구입니다.

다른 팁

C#4에서 작동합니다 IEnumerable<T>, 당신은 할 수 있습니다 :

IEnumerable<Animal> animals = new List<Giraffe>();

하지만 List<T> 공변량 투영이 아니므로 위에서 수행 한대로 목록을 할당 할 수 없습니다.

List<Animal> animals = new List<Giraffe>();
animals.Add(new Monkey());

분명히 유효하지 않습니다.

측면에서 List<T>, 나는 당신이 운이 좋지 않은 것을 두려워합니다. 그러나 .NET 4.0/C# 4.0은 공분산/비밀 인터페이스에 대한 지원을 추가합니다. 구체적으로, IEnumerable<T> 이제 정의됩니다 IEnumerable<out T>, 이는 이제 유형 매개 변수가 이제임을 의미합니다 공분산.

이것은 C# 4.0에서 이와 같은 일을 할 수 있음을 의미합니다 ...

// implicit casting
IEnumerable<Animal> animalsList = new List<Giraffe>();

// explicit casting
IEnumerable<Animal> animalsList2 = (IEnumerable<Animal>) new List<Giraffe>();

참고 : 배열 유형은 또한 공분산이되었습니다 (적어도 .NET 1.1 이후).

분산 지원이 추가되지 않은 것은 부끄러운 일이라고 생각합니다. IList<T> 그리고 다른 유사한 일반적인 인터페이스 (또는 일반 클래스조차), 그러나, 적어도 우리는 무언가를 가지고 있습니다.

다른 사람들이 언급했듯이, Covrariance/Contravariance는 변이 가능한 컬렉션에서 지원할 수 없습니다. 그러나 C# 3.5에서 빠른 일방 통행 변환을 수행 할 수 있습니다.

List<Giraffe> giraffes = new List<Giraffe>();
List<Animal> animals = giraffes.Cast<Animal>().ToList();

물론 그것은 같은 것이 아닙니다. 실제로 공분산은 아닙니다. 실제로 다른 목록을 만들고 있지만 "해결 방법"입니다.

.NET 2.0에서는 배열 공분산을 활용하여 코드를 단순화 할 수 있습니다.

List<Giraffe> giraffes = new List<Giraffe>();
List<Animal> animals = new List<Animal>(giraffes.ToArray());

그러나 당신이 실제로 창조하고 있음을 알아야합니다 여기에 새로운 컬렉션.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top