문제

일반 분산 C#4.0 구현되었습니다 그런 방법으로 그의 작성이 가능하 다음과 같은 예외 없이(는 무슨 일이 일어날 C#3.0):

 List<int> intList = new List<int>();
 List<object> objectList = intList; 

[예 non-기능:보 존 스키트의 답변]

나는 최근에 참석하는 회의 존 스키트 준 훌륭한 개요를 일반적인 분산,하지만 난 모르겠어요 나는 완전히 받고 그 이해의 중요성 inout 핵심 용어에 관해서 콘트라 와 co-차지만,저는 호기심을 어떻게 됩니다.

무엇 CLR 볼 때 이 코드를 실행? 그것은 암시적으로 변환 List<int> 하기 List<object> 또는 간단한 건축에서는 우리는 이제 사이에 변환 파생된 형식에 부모님 종류?

나의 관심사,이유 없이 도입 이전 버전에는 무슨의 주요 혜택-즉 실제 사용?

이에 대한 자세한 정보를 원 게시 에 대한 일반 분산(그러나 문제는 매우 오래된 찾고,실제,up-to-date information)

도움이 되었습니까?

해결책

No,의 예로 작동하지 않을 것에 대한 세 가지 이유:

  • 클래스(예: List<T> 용)invariant;만 대표와 인터페이스 변종
  • 에 대한 분산업,인터페이스에만 사용하여 입력에서 매개 변수 하나의 방향으로(에 대한 반공해 공분산)
  • 값을 유지 않으로 지원되는 유형에 대해 인수 분산-이 없다,그래서 converstion 서 IEnumerable<int> 하기 IEnumerable<object> 예를 들어

(코드를 컴파일하지 못합니다 모두에서는 C#3.0 4.0-예외가 없습니다.)

그래서 이 작업:

IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings;

CLR 만 사용하는 기준,변경-새로운 객체를 만듭니다.그래서 만약 당신 objects.GetType() 당신은 아직도 List<string>.

나는 그것을 믿을 도입하지 않았기 때문에 이전의 언어 디자이너도 있었을 밖으로 작동 상세정보를 노출하는 방법의-그것은에서 CLR 이후 v2.

장점과 같은 다른 시간에 당신이 원하는 곳에 사용할 수 있도록 하나 형식으로 다른입니다.사용 예로 사용되는 마지막 주 토요일 경우,당신이 무언가를 구현하는 IComparer<Shape> 을 비교하양 지역에 의하여,그것은 미친 짓이 사용할 수 없다는 것을 정렬 List<Circle> -는 경우 그 비교할 수 있습니다 어떤 두 개의 모양,그것은 확실히 비교해 두 개의 원입니다.로의 C#4,이 될 거라고는 반공변에서 변환 IComparer<Shape> 하기 IComparer<Circle> 할 수 있도록 전화 circles.Sort(areaComparer).

다른 팁

몇 가지 추가 생각.

이 코드가 실행되면 CLR은 무엇을 보나요

Jon과 다른 사람들이 올바르게 언급했듯이, 우리는 수업에 차이가없고 인터페이스와 대표 만 있습니다. 따라서 귀하의 예에서 CLR은 아무것도 보지 못합니다. 해당 코드는 컴파일되지 않습니다. 충분한 캐스트를 삽입하여 컴파일하도록 강요하면 캐스트 예외가 잘못되어 런타임에 충돌합니다.

이제 여전히 차이가 작동 할 때 어떻게 차이가 어떻게 작동하는지 묻는 것이 여전히 합리적인 질문입니다. 대답은 다음과 같습니다. 우리가 이것을 인터페이스를 매개 변수화하는 참조 유형 인수로 제한하는 이유는 아무것도 아님 무대 뒤에서 발생합니다. 당신이 말할 때

object x = "hello";

무대 뒤에서 일어나는 일은 문자열에 대한 참조가 유형 객체의 변수에 붙어 있다는 것입니다. 수정없이. 문자열에 대한 참조를 구성하는 비트는 객체에 대한 참조가되는 법적 비트이므로 여기서는 아무 일도 할 필요가 없습니다. CLR은 단순히 그 비트를 문자열을 언급하는 것으로 생각하는 것을 멈추고 객체를 언급하는 것으로 생각하기 시작합니다.

당신이 말할 때:

IEnumerator<string> e1 = whatever;
IEnumerator<object> e2 = e1;

같은 것. 아무 반응이 없습니다. 문자열 열거자를 참조하는 비트는 객체 열거자를 참조하는 비트와 동일합니다. 캐스트를 할 때 다소 더 많은 마법이 있습니다.

IEnumerator<string> e1 = whatever;
IEnumerator<object> e2 = (IEnumerator<object>)(object)e1;

이제 CLR은 E1이 실제로 해당 인터페이스를 구현하는 수표를 생성해야하며 해당 점검은 분산 인식에 대해 현명해야합니다.

그러나 우리가 변형 인터페이스로 도망 갈 수있는 이유는 단지 No-OP 변환이기 때문입니다. 왜냐하면 정기적 인 할당 호환성이 그렇게됩니다. E2를 위해 무엇을 사용 하시겠습니까?

object z = e2.Current;

그것은 문자열을 참조하는 비트를 반환합니다. 우리는 이미 그것들이 변화없이 객체와 호환 될 수 있음을 이미 확립했습니다.

왜 이것이 이전에 소개되지 않았습니까? 우리는해야 할 다른 기능과 한정된 예산이있었습니다.

원칙 혜택은 무엇입니까? 이 순서에서 일련의 객체 "Just Work"시퀀스로 변환됩니다.

관심이없는 이유는 이전 버전에서 소개되지 않은 이유

.NET의 첫 번째 버전 (1.x)에는 제네릭이 전혀 없었기 때문에 일반적인 차이가 멀었습니다.

모든 버전의 .NET에는 배열 공분산이 있습니다. 불행히도 안전하지 않은 공분산입니다.

Apple[] apples = new [] { apple1, apple2 };
Fruit[] fruit = apples;
fruit[1] = new Orange(); // Oh snap! Runtime exception! Can't store an orange in an array of apples!

C# 4의 공동 및 분량은 안전 하며이 문제를 방지합니다.

주요 이점은 무엇입니까? 즉 실제 사용법은 무엇입니까?

코드에서 여러 번 API를 호출하는 것은 증폭 된 유형의 기반을 기대합니다 (예 : IEnumerable<Base>)하지만 당신이 가진 것은 증폭 된 유형의 파생입니다 (예 : IEnumerable<Derived>).

C# 2 및 C# 3에서는 수동으로 변환해야합니다. IEnumerable<Base>, "그냥 작동해야한다". 공동 및 콘트라 분산은 그것을 "그냥 작동"하게 만듭니다.

추신 : Skeet의 대답은 내 모든 담당자 포인트를 먹는다는 것을 완전히 짜증납니다. 젠장, 스키트! :-) 그가있는 것 같아 전에 이것에 대답했습니다, 그렇지만.

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