2011-12-19 4 views
3

대 목록에 Contravariance는 그래서, 내가 가진 가정 해 봅시다 :공분산 및 IEnumerable을

Public Interface ISomeInterface 

End Interface 

Public Class SomeClass 
    Implements ISomeInterface 

End Class 

을 내가 MyListList(Of SomeClass)로, 내가 직접 List(Of ISomeInterface) = MyList를 설정할 수없는 경우. 그러나 IEnumerable(Of ISomeInterface) = MyList을 설정할 수 있습니다.

공역에 대한 나의 이해와 함께 나는 List(Of T)IEnumerable(Of T)을 구현하기 때문에 목록에 나열해야한다고 생각했다. 분명히, 나는 뭔가를 놓치고있다.

왜 그렇게 작동합니까? 특히 다음과 같은 것을 할 수없는 이유는 무엇입니까?

Dim Animals As new List(Of Animal) 
Dim Cats As List(Of IAnimal) = Animals 

여기서 Animal은 IAnimal 인터페이스를 구현합니다. 하지만 수행 할 수 있습니다

Dim Animals As New List(Of Animal) 
Dim Cats As IEnumerable(Of IAnimal) = Animals 
+4

http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/ – Oded

+0

몇 가지 기사를 읽었으며 그 중 어떤 것도이 특정 사례에 대한 답변을 제공하지 못했습니다. 특정 유형의 목록에서 인터페이스 목록으로 직접 이동할 수있는 이유를 다루는 특정 기사 또는 그 부분을 지적 해 주시겠습니까? – Jay

+0

http://blogs.msdn.com/b/ericlippert/archive/2007/10/26/covariance-and-contravariance-in-c-part-five-interface-variance.aspx – Oded

답변

5

내가 이전에 웹에이 문제를 해결 많은 정보를보고 기억합니다, 그래서 난 내 대답이 정말로 새로운 것을 추가 할지는 모르지만 나는 시도 할 것입니다.

.NET 4를 사용하는 경우 IEnumerable (Of T)의 정의가 실제로 IEnumerable (Of Out T)인지 확인하십시오. 새로운 Out 키워드는이 인터페이스의 공분산을 나타내는 버전 4에서 소개되었습니다. 그러나 List (Of T) 클래스는 단순히 List (Of T)로 정의됩니다. 여기서 Out 키워드는 사용되지 않으므로 클래스는 공 변하지 않습니다.

설명하는 것과 같은 특정 과제를 수행 할 수없는 이유를 설명하기 위해 몇 가지 예를 제공합니다. 귀하의 질문은 VB에서 작성된 것을 볼 수 있으므로 C# 사용에 대한 사과드립니다.

 List<Car> cars = new List<Car>(); 

또한 목록을 만들 수 있습니다 : 당신은 객체가 자동차에서 파생 포함 할 수 차의 목록을 만들 수 있습니다

abstract class Vehicle 
{ 
    public abstract void Travel(); 
} 

class Car : Vehicle 
{ 
    public override void Travel() 
    { 
     // specific implementation for Car 
    } 
} 

class Plane : Vehicle 
{ 
    public override void Travel() 
    { 
     // specific implementation for Plane 
    } 
} 

:

는 다음과 같은 클래스가 있다고 가정 평면에서 파생 된 객체 만 포함 할 수있는 평면 :

 List<Plane> planes = new List<Plane>(); 

목록을 만들 수도 있습니다 자동차에서 파생 된 개체를 포함 할 수있는 차량의 :

 List<Vehicle> vehicles = new List<Vehicle>(); 

그것은 자동차의 목록에 자동차를 추가하는 법, 그리고이 평면의 목록에면을 추가하는 법입니다. 또한 차량 목록에 자동차와 비행기를 모두 추가하는 것도 합법입니다.따라서, 다음 코드 줄은 모두 유효합니다

 cars.Add(new Car()); // add a car to the list of cars 

     planes.Add(new Plane()); // add a plane to the list of planes 

     vehicles.Add(new Plane()); // add a plane to the list of vehicles 
     vehicles.Add(new Car()); // add a car to the list of vehicles 

그것은 평면의 목록에 자동차를 추가하는 법률이 아니다 않으며 법적 자동차의 목록에면을 추가하는 것입니다. 코드의 다음 줄은 컴파일되지 않습니다 :

 vehicles = cars; // This is not allowed 
     vehicles.Add(new Plane()); // because then you could do this 
:

 cars.Add(new Plane()); // can't add a plane to the list of cars 
     planes.Add(new Car()); // can't add a car to the list of planes 

따라서, 자동차의 목록 또는 변수 차량에 평면의 목록을 지정하여이 제한을 우회하려고 법적 아니다

위에서 두 줄의 코드가 무엇인지 생각해보십시오. vehicles 변수는 실제로 List<Car> 객체이며 자동차에서 파생 된 객체 만 포함해야합니다. 그러나 List<Vehicle>에 Add (Vehicle) 메서드가 포함되어 있으므로 이론적으로 Plane 개체를 List<Car> 컬렉션에 추가 할 수는 있지만 이는 정확하지 않습니다.

그러나 자동차 목록이나 비행기 목록을 IEnumerable<Vehicle> 변수에 할당하는 것은 완전히 유효합니다.

 IEnumerable<Vehicle> vehicles = cars; 

     foreach (Vehicle vehicle in vehicles) 
     { 
      vehicle.Travel(); 
     } 

여기서 간단한 설명은 IEnumerable 인터페이스로 컬렉션을 조작 할 수 없다는 것입니다. 그것은 본질적으로 읽기 전용 인터페이스입니다. T 개체 (이 경우 Vehicle)는 IEnumerable 인터페이스의 Current 속성에서 반환 값으로 만 표시됩니다. Vehicle 객체를 입력 매개 변수로 사용하는 메서드가 없으므로 컬렉션이 불법적으로 수정 될 위험이 없습니다.

사이드 노트 : IList<T> 인터페이스가 IReadableList<out T> 인터페이스와 IWritableList<in T> 인터페이스의 합성물 인 것이 당연하다고 생각했습니다.

+0

훌륭한 세부 사항에 감사드립니다. – Jay

+0

나는 그것들을'IReadableByIndex '와'IAppendable '이라고 부르기는하지만, 읽기와 쓰기 인터페이스를 분리하는 것이 합리적이라는 것에 동의한다. 'List '는 (IAppendable .Add (T))의 구현으로 간주 될 수 있기 때문에 호환성을 손상시키지 않고' 현재 읽기 - 쓰기'List .this [int]의 구현이 읽기 전용'IReadableByIndex .this [int]'의 구현으로 간주 될 수있는 방법이 없습니다. – supercat

+0

@supercat 의견을 보내 주셔서 감사합니다! 당신과 동의...나는 처음에 그 생각이 나에게 일어 났을 때 고심했다. 처음에는 "IReadOnlyList"라인을 생각하고 있었지만, 언급 한 바와 같이'List '를 읽기 전용리스트라고 부르는 것은 사실이 아니다. 그때 나는 IReadableList 인터페이스를 호출하는 것으로 생각을 바꿨습니다. 기본 유형이 읽기 전용 모음임을 주장하지 않습니다. 원하는 경우 인터페이스에 읽을 수있는 부분 만 정의하여 소비자에게보다 타겟 화 된 인터페이스를 제공 할 수 있습니다. –

2
그것은 당신이 List(Of ISomeInterface) 변수에 할당 한 후에는 List(Of SomeClass)로 무엇을 할 수 있는지에 대해 생각하는 데 도움이 될

.

ISomeInterface을 구현하는 모든 개체를 추가 할 수 있습니다 (예 : SomeOtherClass, 더 이상이없는 유효한 List(Of SomeClass)

이 공분산이 경우 List(Of T)에 대해 정의되지 않는 이유는