.NET 4.5두 개의 새로운 컬렉션 인터페이스인 IReadOnlyList와 IReadOnlyDictionary가 추가되었습니다. 표면적으로는 평범해 보일 수 있지만, 하위 호환성, 상호운용성, 그리고 공동가변성의 역할에 관한 꽤 복잡한 이야기를 드러냅니다.
IReadOnlyList와 IReadOnlyDictionary는 .NET 개발자들이 항상 갖고 싶어 했던 두 가지 인터페이스입니다. 읽기 전용 인터페이스는 대칭성을 제공하는 것 외에도(쓰기 가능한 인터페이스와 달리) NotSupportedException 예외만 던지고 아무 일도 하지 않는 메서드 구현을 없애야 합니다. 이 모든 것은 시간 제약으로 인해 완성되지 못했다.
다음 기회가 왔다. NET 2.0. 이를 통해 마이크로소프트는 약한 타입의 컬렉션을 은퇴시키고 강한 타입의 피어 컬렉션으로 대체할 수 있습니다. 하지만 기본 라이브러리[1] 팀은 다시 한 번 읽기 전용 목록을 제공할 기회를 놓쳤습니다. 킷 조지는 이렇게 썼습니다.
Joe와 함께 말씀하신 문제에 대해 인터페이스를 제공하는 대신 기본 구현을 제공할 예정이므로, ReadOnlyCollectionBase 기본 클래스를 제공합니다. 하지만 강한 타입이 아니기 때문에 사람들이 사용을 꺼리는 이유도 이해할 수 있습니다. 하지만 제네릭 도입과 함께 ReadOnlyCollection도 추가<T>되어 동일한 기능뿐 아니라 강력한 타입도 제공합니다: 훌륭합니다!
ReadOnlyCollection<T>은 밀봉된 클래스가 아니므로, 필요하다면 자신의 컬렉션을 전속력으로 작성할 수 있습니다. 이 컬렉션들은 일반적인 필요에 맞게 조정할 수 있기 때문에, 동일한 개념에 대한 인터페이스를 도입할 계획은 없습니다. 크시슈토프 왈리나도 이 주제에 대해 자신의 의견을 밝혔다.
놀랍지 않겠지만, IList와 IList<T>는 읽기 전용 컬렉션에 사용할 두 인터페이스입니다. 두 것 모두 IsReadOnly 불리언 속성을 가지고 있는데, 읽기 전용 컬렉션이 이 속성을 구현하면 true로 반환되어야 합니다. 순수 읽기 전용 인터페이스를 추가하지 않는 이유는 기본 라이브러리에 불필요한 복잡성을 너무 많이 더한다고 느끼기 때문입니다. 복잡성 측면에서 우리는 이 새로운 인터페이스와 그 소비자 모두를 의미합니다.
API 디자이너가 런타임에 IsReadOnly 속성과 그로 인해 발생할 수 있는 예외를 확인하는 데 신경 쓰지 않는다면, 이 경우 IList 인터페이스를 사용하는 것이 괜찮다고 생각합니다; 만약 한 번에 정말 깔끔한 맞춤형 API를 제공할 의향이 있다면, 이 경우 IList 인터페이스 구현을 보여주고 맞춤형 읽기 전용 API를 공개해야 합니다. 후자는 객체 모델에서 노출된 컬렉션에서 일반적으로 사용됩니다. 개발자들이 이 상황에 대해 불만을 제기했지만, 제네릭이 제공하는 새로운 기회가 이 핵심 문제를 훨씬 능가하며 문제는 . NET 4는 이전에는 거의 무시당했어요. 하지만 이 결정은 일부 반응을 불러일으켰으며, 이에 대해서는 나중에 다룰 예정입니다.
안으로 들어가는 사람. .NET 4의 흥미로운 새 기능이 런타임에 추가되었습니다. 이전 버전에서. .NET에서 인터페이스가 타입이 될 때, 그 인터페이스들은 과도하게 제한됩니다. 예를 들어, 고객이 Person로부터 상속받더라도, IEnumerable 타입의 객체를 <Customer>IEnumerable 타입의 함수에 매개변수로 전달하는 것은 불가능합니다<Person>. 공변 지원이 추가되면서 이 제한은 부분적으로 해제되었습니다.
"부분적으로"라고 말하는 이유는, 경우에 따라 IEnumuable 인터페이스를 사용하는 대신 풍부한 API가 포함된 인터페이스를 동시에 사용하는 것이 좋기 때문입니다. IList 인터페이스가 공변적이지 않더라도, 읽기 전용 리스트 인터페이스는 공변적이어야 합니다. 안타깝게도, . .NET 기본 라이브러리 팀은 이 간과를 다시 다루지 않기로 결정했습니다.
그러다 WinRT의 도입과 COM의 부활이 모든 것을 바꿨습니다. COM 상호운용성은 한때 개발자들이 다른 선택지가 없을 때 사용하던 기술이었지만, 이제는 . .NET 프로그래밍의 초석입니다. 그리고 WinRT가 IVectorView<T>와 IMapView<K, V> 인터페이스를 노출하기 때문에 .NET 역시 이에 맞게 조정되어야 합니다.
WinRT 프로그램의 흥미로운 특징 중 하나는 각 개발 플랫폼마다 다르지만 유사한 API를 발표한다는 점입니다. 이미 알고 계시겠지만, 모든 메서드 이름은 카멜케이시드 [2]로 표현되며, C++ 및 .NET 개발자들은 메서드 이름을 파스칼케이드[3]로 봅니다. 또 다른 더 극적인 변화는 C++와 .NET 인터페이스 간의 자동 매핑입니다. 그래서. .NET 개발자는 Windows.Foundation.Collections 네임스페이스를 다룰 필요 없고, System.Collections.Generic 네임스페이스를 계속 사용하면 됩니다. IVectorView<T>와 IMapView<K, V> 인터페이스는 런타임에 따라 각각 IReadOnlyList <T>인터페이스와 IReadOnlyDictionary<TKey, TValue> 인터페이스로 변환됩니다.
C++/WinRT의 인터페이스 이름들이 어느 정도 더 정확하다는 점도 주목할 만합니다. 이 인터페이스들은 컬렉션의 일부 뷰를 표현하는 데 사용되지만, 인터페이스는 컬렉션 자체가 불변인지 보장하지 않습니다. 경험이 있는 사람들 사이에서도요. .NET 개발자들 사이에서 흔히 저지르는 실수 중 하나는 ReadOnlyCollection 타입이 변경 불가능한 컬렉션의 복사본이라고 생각하는 것인데, 실제로는 활성 컬렉션의 래퍼일 뿐입니다(읽기 전용, 동결, 변경 불가능한 컬렉션에 대한 자세한 내용은 Andrew Arnott의 동명 게시물을 참고하세요).
IList <T>인터페이스는 IReadOnlyList 인터페이스와 동일한 모든 구성원을 가지고 <T>있으며, 모든 IList 타입 <T>리스트는 읽기 전용 리스트로 표현할 수 있지만, IList<T>는 IReadOnlyList로부터 상속받지 않<T>아 이 점에 대해 알아두면 흥미로울 수 있습니다. 임모 랜드워스가 설명했다,
이 방법이 작동하는 이유는 읽기 전용 인터페이스가 순수한 읽기-쓰기 인터페이스의 하위 집합이기 때문인데, 이는 합리적인 가정처럼 보입니다. 안타깝게도 이 가정은 현실과 일치하지 않는데, 각 인터페이스의 메타데이터 수준 메서드마다 고유한 슬롯이 있어 명시적 인터페이스 구현이 작동하기 때문입니다. 즉, 읽기 전용 인터페이스를 어떤 변수 클래스 기본 클래스로 도입할 수 있는 유일한 방법은 로 폴백하는 것입니다. NET 2.0, 즉 원래 구상된 시기를 의미합니다. 완전히 배포되면 공변 및/또는 인버터 마커(VB와 C#에서는 "in"과 "out"로 표현됨)만 추가할 수 있습니다.
왜 IReadOnlyCollection 인터페이스가 없느냐는 질문에 <T>Immo는 이렇게 답했습니다.
이 설계를 고려했지만, Count 속성만 제공하는 타입을 추가하는 것은 기본 라이브러리에 큰 가치를 더하지 못할 것 같았습니다. 기본 라이브러리 팀에서는 API가 -1000에서 시작한다면, 어떤 가치를 제공하는 것만으로는 추가를 정당화할 수 없다고 믿습니다. 새로운 API를 추가하는 이유도 비용이 듭니다. 예를 들어, 개발자들이 선택할 수 있는 개념이 더 많아질 수 있기 때문입니다. 처음에는 이 타입을 추가하면 단순히 카운트를 얻고 흥미로운 작업을 하려는 특정 상황에서 코드 성능이 향상될 거라 생각했습니다. 예를 들어, 기존 컬렉션에 대량 추가하는 경우. 하지만 이러한 상황에서는 IEnumerable 인터페이스만 사용하도록 권장하며<T>, <T>ICollection 인터페이스를 구현하는 인스턴스를 특별한 경우에만 사용할 것을 권장했습니다. 모든 내장 컬렉션 유형이 이 인터페이스를 구현한 이후로, 가장 일반적인 시나리오에서 성능 향상은 없었습니다. 참고로, <T>IEnumerable의 확장 메서드인 Count()도 이 기능을 수행할 수 있습니다. 이 새로운 인터페이스들은 에 대해 제공됩니다. NET 4.5와 . NET for Windows 8.
번역 주석
[1] 기본급 도서관, 약칭 BCL. 기본 클래스 라이브러리에 대한 자세한 정보는 MSDN에 참여해 주세요.
[2] 낙타 케이스드, 혹 명명법, 하부 낙타 케이스라고도 불립니다. 이 형식은 첫 단어가 소문자로 시작하는 것입니다; 두 번째 단어의 첫 글자는 대문자로 표기합니다. 예를 들어 firstName, lastName.
[3] 파스칼 케이스드, 파스칼 명명법, 상부 낙타 케이스로도 알려져 있습니다. 형식은 각 단어의 첫 글자를 대문자로 표기하는 것입니다. 예를 들어 FirstName, LastName, CamelCase가 있습니다. |