C #에서 읽고있는 교과서에서 이러한 문제를 발견했지만 컨텍스트 부족으로 인해 이해하는 데 어려움이 있습니다.
그것들이 무엇인지, 그리고 그들이 밖에서 무엇을 위해 유용한 지에 대한 간결한 설명이 있습니까?
명확히하기 위해 편집 :
공변 인터페이스 :
interface IBibble<out T>
.
.
반 변성 인터페이스 :
interface IBibble<in T>
.
.
C #에서 읽고있는 교과서에서 이러한 문제를 발견했지만 컨텍스트 부족으로 인해 이해하는 데 어려움이 있습니다.
그것들이 무엇인지, 그리고 그들이 밖에서 무엇을 위해 유용한 지에 대한 간결한 설명이 있습니까?
명확히하기 위해 편집 :
공변 인터페이스 :
interface IBibble<out T>
.
.
반 변성 인터페이스 :
interface IBibble<in T>
.
.
답변:
를 사용 <out T>
하면 인터페이스 참조를 계층 구조에서 상위로 취급 할 수 있습니다.
를 사용 <in T>
하면 인터페이스 참조를 계층 구조에서 아래쪽으로 처리 할 수 있습니다.
좀 더 영어로 설명해 보겠습니다.
동물원에서 동물 목록을 검색하고 있으며이를 처리하려고한다고 가정 해 보겠습니다. 동물원에있는 모든 동물에는 이름과 고유 ID가 있습니다. 어떤 동물은 포유류이고, 어떤 것은 파충류이고, 어떤 것은 양서류이고, 어떤 것은 물고기입니다. 그러나 그들은 모두 동물입니다.
따라서 동물 목록 (다른 유형의 동물 포함)을 사용하면 모든 동물에 이름이 있다고 말할 수 있으므로 모든 동물의 이름을 알아내는 것이 안전 할 것입니다.
그러나 물고기 목록 만 있지만 동물처럼 취급해야하는 경우에는 어떻게됩니까? 직관적으로 작동하지만 C # 3.0 이전에서는이 코드 조각이 컴파일되지 않습니다.
IEnumerable<Animal> animals = GetFishes(); // returns IEnumerable<Fish>
그 이유는 컴파일러가 동물 컬렉션을 검색 한 후 의도 한 바를 "알지"못하거나 할 수 있기 때문입니다. 아는 한, IEnumerable<T>
개체를 목록에 다시 넣는 방법이있을 수 있으며, 그러면 잠재적으로 물고기가 아닌 동물을 물고기 만 포함해야하는 컬렉션에 넣을 수 있습니다.
즉, 컴파일러는 이것이 허용되지 않는다고 보장 할 수 없습니다.
animals.Add(new Mammal("Zebra"));
따라서 컴파일러는 코드 컴파일을 완전히 거부합니다. 이것은 공분산입니다.
반공 변성을 살펴 보겠습니다.
우리 동물원은 모든 동물을 다룰 수 있기 때문에 확실히 물고기를 다룰 수 있으므로 동물원에 물고기를 추가해 보겠습니다.
C # 3.0 이하에서는 컴파일되지 않습니다.
List<Fish> fishes = GetAccessToFishes(); // for some reason, returns List<Animal>
fishes.Add(new Fish("Guppy"));
여기서 컴파일러 는List<Animal>
모든 물고기가 동물이기 때문에 메서드가 단순히 반환 하더라도이 코드 조각을 허용 할 수 있으므로 유형을 다음과 같이 변경하면됩니다.
List<Animal> fishes = GetAccessToFishes();
fishes.Add(new Fish("Guppy"));
그러면 작동하지만 컴파일러는이 작업을 수행하지 않는지 확인할 수 없습니다.
List<Fish> fishes = GetAccessToFishes(); // for some reason, returns List<Animal>
Fish firstFist = fishes[0];
목록은 실제로 동물 목록이므로 허용되지 않습니다.
따라서 반대 및 공분산은 객체 참조를 처리하는 방법과이를 사용하여 수행 할 수있는 작업입니다.
C # 4.0 의 in
및 out
키워드는 특히 인터페이스를 둘 중 하나로 표시합니다. 를 사용 in
하면 제네릭 유형 (일반적으로 T)을 입력 위치에 배치 할 수 있습니다. 이는 메서드 인수 및 쓰기 전용 속성을 의미합니다.
를 사용하면 출력 위치에 out
제네릭 유형을 배치 할 수 있습니다. 이는 메소드 반환 값, 읽기 전용 속성 및 out 메소드 매개 변수입니다.
이렇게하면 코드로 의도 한 작업을 수행 할 수 있습니다.
IEnumerable<Animal> animals = GetFishes(); // returns IEnumerable<Fish>
// since we can only get animals *out* of the collection, every fish is an animal
// so this is safe
List<T>
T에 대한 내부 및 외부 방향이 모두 있으므로 공변도 반 변이도 아니지만 다음과 같이 객체를 추가 할 수있는 인터페이스입니다.
interface IWriteOnlyList<in T>
{
void Add(T value);
}
다음과 같이 할 수 있습니다.
IWriteOnlyList<Fish> fishes = GetWriteAccessToAnimals(); // still returns
IWriteOnlyList<Animal>
fishes.Add(new Fish("Guppy")); <-- this is now safe
다음은 개념을 보여주는 몇 가지 비디오입니다.
예를 들면 다음과 같습니다.
namespace SO2719954
{
class Base { }
class Descendant : Base { }
interface IBibbleOut<out T> { }
interface IBibbleIn<in T> { }
class Program
{
static void Main(string[] args)
{
// We can do this since every Descendant is also a Base
// and there is no chance we can put Base objects into
// the returned object, since T is "out"
// We can not, however, put Base objects into b, since all
// Base objects might not be Descendant.
IBibbleOut<Base> b = GetOutDescendant();
// We can do this since every Descendant is also a Base
// and we can now put Descendant objects into Base
// We can not, however, retrieve Descendant objects out
// of d, since all Base objects might not be Descendant
IBibbleIn<Descendant> d = GetInBase();
}
static IBibbleOut<Descendant> GetOutDescendant()
{
return null;
}
static IBibbleIn<Base> GetInBase()
{
return null;
}
}
}
이러한 표시가 없으면 다음이 컴파일 될 수 있습니다.
public List<Descendant> GetDescendants() ...
List<Base> bases = GetDescendants();
bases.Add(new Base()); <-- uh-oh, we try to add a Base to a Descendant
아니면 이거:
public List<Base> GetBases() ...
List<Descendant> descendants = GetBases(); <-- uh-oh, we try to treat all Bases
as Descendants
이 게시물 은 내가 주제에 대해 읽은 것 중 최고입니다.
간단히 말해서, 공분산 / 반 변성 / 불변은 자동 유형 캐스팅 (기본에서 파생으로 또는 그 반대)을 다룹니다. 이러한 유형 캐스트는 캐스트 된 객체에 대해 수행되는 읽기 / 쓰기 작업과 관련하여 일부 보장이 존중되는 경우에만 가능합니다. 자세한 내용은 게시물을 읽어보십시오.