문제

언어 이론에서 공분산과 비조화의 개념을 설명 할 수 있습니까?

도움이 되었습니까?

해결책

공분산 일부 컬렉션 클래스의 관점에서 매우 간단하고 최고의 생각입니다. List. 우리는 할 수 있습니다 매개 변수화 그만큼 List 일부 유형 매개 변수가있는 클래스 T. 즉, 목록에는 유형의 요소가 포함되어 있습니다 T 일부 T. 목록은 다음과 같은 경우 공분산입니다

s는 t iff 목록의 하위 유형입니다 [s]는 목록 [t]의 하위 유형입니다.

(수학적 정의를 사용하는 곳 IFF 의미합니다 경우에만.)

그것은 List[Apple] a List[Fruit]. 수락하는 일상이있는 경우 a List[Fruit] 매개 변수로, 나는 a List[Apple], 그런 다음 이것을 유효한 매개 변수로 전달할 수 있습니다.

def something(l: List[Fruit]) {
    l.add(new Pear())
}

컬렉션 수업 인 경우 List 돌연변이가 가능하면, 공분산은 우리의 일상이 위와 같이 다른 과일 (사과가 아닌)을 추가 할 수 있다고 가정 할 수 있기 때문에 의미가 없습니다. 따라서 우리는 만 좋아해야합니다 불변 공분산이 될 수있는 수집 수업!

다른 팁

다음은 C# 4.0에 새로운 분산 기능을 추가 한 방법에 대한 기사입니다. 바닥에서 시작하십시오.

http://blogs.msdn.com/ericlippert/archive/tags/covariance+and+contravariance/default.aspx

그 사이에는 구별이 있습니다 공분산 그리고 비밀.
매우 대략적으로, 유형의 순서를 보존하면 조작은 공분산입니다. 반전 이 순서.

순서 자체는보다 일반적인 유형보다 더 큰 일반적인 유형을 나타 내기위한 것입니다.
다음은 C#이 공분산을 지원하는 상황의 한 예입니다. 첫째, 이것은 다양한 객체입니다.

object[] objects=new object[3];
objects[0]=new object();
objects[1]="Just a string";
objects[2]=10;

물론 결국에는 모두 파생되기 때문에 배열에 다른 값을 삽입 할 수 있습니다. System.Object .NET 프레임 워크에서. 다시 말해, System.Object 매우 일반적인 것입니다 크기가 큰 유형. 이제 공분산이 지원되는 곳이 있습니다.
작은 유형의 값을 더 큰 유형의 변수에 할당

string[] strings=new string[] { "one", "two", "three" };
objects=strings;

변수 객체는 유형입니다 object[], 실제로 유형의 값을 저장할 수 있습니다. string[].

생각해보십시오. 어느 쪽이든, 그것은 당신이 기대하는 것이지만 다시는 그렇지 않습니다. 결국 string ~에서 얻다 object, string[] 하지 않습니다 파생 object[]. 이 예에서 공분산에 대한 언어 지원은 어쨌든 과제를 가능하게합니다. 이는 많은 경우에 찾을 수있는 것입니다. 변화 언어가보다 직관적으로 작동하게하는 기능입니다.

이 주제에 대한 고려 사항은 매우 복잡합니다. 예를 들어, 이전 코드를 기반으로 오류가 발생하는 두 가지 시나리오가 있습니다.

// Runtime exception here - the array is still of type string[],
// ints can't be inserted
objects[2]=10;

// Compiler error here - covariance support in this scenario only
// covers reference types, and int is a value type
int[] ints=new int[] { 1, 2, 3 };
objects=ints;

비밀의 작업의 예는 조금 더 복잡합니다. 이 두 수업을 상상해보십시오.

public partial class Person: IPerson {
    public Person() {
    }
}

public partial class Woman: Person {
    public Woman() {
    }
}

Woman 파생됩니다 Person, 확실히. 이제이 두 가지 기능이 있다고 생각합니다.

static void WorkWithPerson(Person person) {
}

static void WorkWithWoman(Woman woman) {
}

함수 중 하나는 Woman, 다른 하나는 더 일반적이며 Person. 에 Woman 사물의 측면, 당신은 이제 이것들을 가지고 있습니다.

delegate void AcceptWomanDelegate(Woman person);

static void DoWork(Woman woman, AcceptWomanDelegate acceptWoman) {
    acceptWoman(woman);
}

DoWork a를 취할 수있는 함수입니다 Woman 그리고 또한 Woman, 그런 다음 인스턴스를 통과합니다 Woman 대의원에게. 고려하다 다형성 여기에있는 요소 중. Person ~이다 더 큰 ~보다 Woman, 그리고 WorkWithPerson ~이다 더 큰 ~보다 WorkWithWoman. WorkWithPerson 또한 고려됩니다 더 큰 ~보다 AcceptWomanDelegate 분산의 목적을 위해.

마지막 으로이 세 줄의 코드가 있습니다.

Woman woman=new Woman();
DoWork(woman, WorkWithWoman);
DoWork(woman, WorkWithPerson);

Woman 인스턴스가 생성됩니다. 그런 다음 Dowork가 호출되어 Woman 인스턴스뿐만 아니라에 대한 참조 WorkWithWoman 방법. 후자는 분명히 대의원 유형과 호환됩니다 AcceptWomanDelegate - 유형의 하나의 매개 변수 Woman, 반환 유형 없음. 그러나 세 번째 줄은 약간 이상합니다. 방법 WorkWithPerson a Person 매개 변수로서 Woman, 요구에 따라 AcceptWomanDelegate. 그럼에도 불구하고, WorkWithPerson 대의원 유형과 호환됩니다. 비밀 가능한 경우 더 큰 유형의 대표의 경우 WorkWithPerson 더 작은 유형의 변수에 저장 될 수 있습니다. AcceptWomanDelegate. 다시 한 번 직관적 인 것입니다. 만약에 WorkWithPerson 어떤 것과도 일할 수 있습니다 Person, a Woman 틀릴 수 없습니다, 오른쪽?

지금 까지이 모든 것이 제네릭과 어떤 관련이 있는지 궁금 할 것입니다. 대답은 분산이 제네릭에도 적용될 수 있다는 것입니다. 사용 된 이전 예제 object 그리고 string 배열. 여기서 코드는 배열 대신 일반 목록을 사용합니다.

List<object> objectList=new List<object>();
List<string> stringList=new List<string>();
objectList=stringList;

이것을 시도하면 이것이 C#의 지원되는 시나리오가 아니라는 것을 알게 될 것입니다. C# 버전 4.0 및 .NET Framework 4.0에서 제네릭의 분산 지원이 정리되었으며 이제 새로운 키워드를 사용할 수 있습니다. 안에 그리고 밖으로 일반 유형 매개 변수. 특정 유형 매개 변수에 대한 데이터 흐름 방향을 정의하고 제한 할 수있어 차이가 작동합니다. 그러나의 경우 List<T>, 유형의 데이터 T 양방향으로 흐르기 - 유형에 메소드가 있습니다. List<T> 그 귀환 T 값과 그러한 값을받는 값.

이러한 방향 제한의 요점은 다음과 같습니다 의미가있는 곳에서 분산을 허용합니다, 그러나 문제를 방지하십시오 이전 배열 예 중 하나에서 언급 된 런타임 오류와 같습니다. 유형 매개 변수가 올바르게 장식 된 경우 안에 또는 밖으로, 컴파일러는 그 차이를 확인하고 허용하거나 허용 할 수 있습니다. 시간을 컴파일하십시오. Microsoft는 .NET Framework의 많은 표준 인터페이스에 이러한 키워드를 추가하기 위해 노력했습니다. IEnumerable<T>:

public interface IEnumerable<out T>: IEnumerable {
    // ...
}

이 인터페이스의 경우 유형의 데이터 흐름입니다 T 개체는 분명합니다. 이 인터페이스에서 지원하는 방법에서만 검색 할 수 있으며 전달되지 않습니다.. 결과적으로, List<T> 이전에 설명했지만 사용 시도 IEnumerable<T> :

IEnumerable<object> objectSequence=new List<object>();
IEnumerable<string> stringSequence=new List<string>();
objectSequence=stringSequence;

이 코드는 버전 4.0이므로 C# 컴파일러에 허용됩니다. IEnumerable<T> 이로 인해 공분산입니다 밖으로 유형 매개 변수의 지정자 T.

일반적인 유형으로 작업 할 때는 차이와 컴파일러가 예상대로 코드가 작동하도록하기 위해 다양한 종류의 속임수를 적용하는 방식을 알고 있어야합니다.

이 장에서 다루는 것보다 분산에 대해 더 많이 알아야 할 것이 있지만, 이는 모든 추가 코드를 이해하기에 충분합니다.

ref :

Bart de Smet은 공분산 및 불안정에 대한 훌륭한 블로그 항목을 가지고 있습니다. 여기.

C#과 CLR은 모두 대의원에게 방법을 바인딩 할 때 참조 유형의 공분산 및 대조 분산을 허용합니다. 공분산은 메소드가 대의원의 반환 유형에서 파생 된 유형을 반환 할 수 있음을 의미합니다. Contra-variance는 메소드가 대의원의 매개 변수 유형의 기반 인 매개 변수를 취할 수 있음을 의미합니다. 예를 들어, 다음과 같이 정의 된 대의원이 주어지면됩니다.

대의원 MyCallback (Filestream S);

프로토 타입으로 된 메소드에 바인딩 된이 대의원 유형의 인스턴스를 구성 할 수 있습니다.

이와 같이:

문자열 somemethod (stream s);

여기서 Somemethod의 리턴 유형 (String)은 대의원의 반환 유형 (개체)에서 파생 된 유형입니다. 이 공분산은 허용됩니다. Somemethod의 매개 변수 유형 (stream)은 Delegate의 매개 변수 유형 (filestream)의 기본 클래스 인 유형입니다. 이 콘트라 분산이 허용됩니다.

공분산 및 비분평은 값 유형이나 공허에 대해서만 참조 유형에 대해서만 지원됩니다. 예를 들어, 다음 방법을 MyCallback 대의원에게 바인딩 할 수 없습니다.

int32 someothermethod (스트림);

일부 이소 메드의 리턴 유형 (int32)은 MyCallback의 리턴 유형 (개체)에서 파생되지만 int32는 값 유형이기 때문에이 형태의 공분산이 허용되지 않습니다.

분명히, 값 유형과 공극이 공분산 및 콘트라 분산에 사용될 수없는 이유는 이러한 것들에 대한 메모리 구조가 다양하기 때문에 참조 유형의 메모리 구조는 항상 포인터이기 때문입니다. 다행히도 C# 컴파일러는 지원되지 않는 작업을 시도하면 오류가 발생합니다.

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