2016-07-17 1 views
1

List.AsReadOnly()IReadOnlyCollection 대신 ReadOnlyCollection을 반환한다는 사실이 나에게 어려움을 겪었습니다. 반환 된 컬렉션은 이 Dictionary이므로 IReadOnlyCollection으로 자동 업데이트 될 수 없습니다. 이것은 이상하게 보였고 .Net 소스 코드를 검토 한 결과 메서드는 List에 대해 Dictionary에 대해 수행하는 것, 즉 인터페이스 대신 구체적인 클래스를 반환하는 작업과 다르다는 것을 확인했습니다.List.AsReadOnly가 ReadOnlyCollection을 반환하지만 Dictionary.AsReadOnly가 IReadOnlyDictionary를 반환하는 이유는 무엇입니까?

이유가 누구인지 설명 할 수 있습니까? 특히 우리가 가능할 때, 특히 공개 할 때 인터페이스를 사용하기를 원하기 때문에 이러한 불일치가 발생하는 것은 당연한 것입니다.

제 코드에서는 소비자가 개인적인 방법 이었기 때문에 IReadOnlyDictionary<T, IReadOnlyCollection<T>>에서 IReadOnlyDictionary<T, ReadOnlyCollection<T>>으로 매개 변수 서명을 변경할 수 있다고 생각했습니다. 그러나, 나는이이 컬렉션 값을 수정할 수있는 개인 방법처럼 보이게 것을 깨달았다, 그래서 제대로 인터페이스를 사용하기 위해 이전의 코드로 성가신 명시 적 캐스트를 넣어 : 이후

.ToDictionary(
    item => item, 
    item => (IReadOnlyCollection<T>) relatedItemsSelector(item) 
     .ToList() 
     .AsReadOnly() // Didn't expect to need the direct cast 
) 

아, 그리고 나는 항상 공분산과 반동을 혼란스럽게하고 있습니다. 누군가 자동 캐스팅을 방해하는 것을 말해 주시겠습니까? 그리고 미래를 생각해내는 방법을 생각 나게 해 주시겠습니까? (예 : 콜렉션은 ______ [input/output] 매개 변수에 대해 ______variant [공동/contra]가 아닙니다.) 인터페이스의 많은 구현이있을 수 있고 모든 요청 된 유형에 대한 사전의 개별 요소 내가이 단순한면조차도 불지 않는 한 을 이해하지 못한다.이 경우에는 내가 올바른 설정을 도울 수 있기를 바랍니다.

답변

0

이유는 역사적입니다. IReadOnly * 인터페이스는 .NET 4.5에 추가되었으며 List<T>.AsReadOnly()은 .NET 2.0에 다시 추가되었습니다. 반환 유형을 변경하는 것은 큰 변화였습니다.

명백한 캐스트가 그렇게 나쁘지 않습니다. 컴파일러는이를 정적으로 검증 할 수 있기 때문에 런타임 캐스트도 아닙니다 (캐스트가 IL에 방출되지 않음). 그런데 목록에 대한 색인 액세스 기능을 제공하는 IReadOnlyList<T>으로 전송할 수도 있습니다. 필요한 유형을 반환하는 확장 메소드 (예 : AsReadOnlyList())를 작성할 수도 있습니다.

{co, contra} 분산에 대해서는 C# 키워드 in (반 변형) 및 out (공변수)을 사용하면 더 쉽게 기억할 수 있습니다. in 형식 매개 변수는 입력 메서드 인수로만 표시 될 수 있으며 out 형식 매개 변수는 출력 (반환 값)으로 만 나타날 수 있습니다. 파라미터를 받아들이는 메소드입니다 (예 : 형식이 Base 인 경우 Derived 유형으로 호출하는 것이 안전하므로 해당 방향으로 in 매개 변수를 캐스팅하는 것이 안전합니다. out은 그 반대입니다. 예를 들어

:

interface IIn<in T> { Set(T value); } 
IIn<Base> b = ... 
IIn<Derived> d = b; 
d.Set(derived); // safe since any method accepting Base can handle Derived 

interface IOut<out T> { T Get(); } 
IOut<Derived> d = ... 
IOut<Base> b = d; 
b.Get(); // safe since any Derived is Base 

비 읽기 전용들이 inout 모두 일해야 할 것 때문에 컬렉션 인터페이스가 -variant *가 될 수없고, 그 안전하지 않은 것입니다. 컴파일러와 CLR은이를 허용하지 않습니다. .NET은 배열에 대해 안전하지 않은 형태로 나타납니다.

var a = new[] { "s" }; 
var o = (object[])a; 
o[0] = 1; // ArrayTypeMismatchException 

일반적인 분산으로 혼란을 피하는 방법을 알 수 있습니다. 아마도 그들은 in (cotravariant) 방향을 허용하는 쓰기 전용 인터페이스를 추가 할 수 있었지만, 나는 그것들에서 많은 가치를 찾지 못했을 것으로 추측하고 있습니다.

+0

오 오 오! 나는 그것이 자동적으로 캐스팅되지 않을 것이라고 가정하고있었습니다. * covariant가 아니었지만, 이제는 IReadOnlyCollection이 읽기 전용이기 때문에 컴파일러는 파생 된 타입에서베이스로 캐스팅하는 것이 안전하다는 것을 알 수 있습니다. . 그렇다면 왜 내가 명시 적 캐스트를해야만 했습니까? – ErikE

+0

공분산과 반공립은 두 개의 일반 * 인터페이스 * 사이에서 캐스팅하고 * 유형 매개 변수 * (기본에서 파생 또는 그 반대로 변경)에서만 발생합니다. 당신이 사용한 캐스트는 캐스트 일뿐입니다.'ReadOnlyCollection '는'IReadOnlyList '를 구현했기 때문에 허용되었습니다. –

+0

아, 그럴리가. 점점 더 명확 해지고 있습니다. 왜 암묵적으로 던지지 못했을까요? – ErikE