프로그램을 개발하다 보면 List나 Dictionary와 같이 제네릭 컬렉션에 타입 매개변수를 지정하여 사용하게 될 것이다. 기존에 사용 중인 컬렉션 타입에 영향을 주지 않으면서 새로운 기능을 추가하고 싶다면 구체화된 컬렉션 타입에 대해 확장 메서드를 작성하면 된다. 예시 : IEnumerable 이번에도 IEnumerable에 이미 구현된 예시가 있다. 타입 매개변수로 특정 숫자 타입이 전달되었을 경우에만 사용할 수 있는 있는 확장 메서드들이 있다. public static class Enumerable { public static int Average(this IEnumerable sequence); public static int Max(this IEnumerable sequence); pu..
확장 메서드를 이용하면 인터페이스에 새로운 동작을 추가할 수 있다. 인터페이스에는 가능한 한 최소한의 기능만을 정의하고, 확장 메서드를 세트로 함께 구현하면 손쉽게 기능을 확장할 수 있다. 특히 API를 추가적으로 정의하지 않고도 새로운 기능을 추가할 수 있다. 예시 : IEnumerable System.Linq.Enumerable 클래스가 이 기법을 활용한 대표적인 예다. System.Enumerable에는 IEnumerable에 정의된 50개 이상의 확장 메서드가 포함되어 있다. Where, OrderBy, GroupInfo 등이 있다. 이처럼 IEnumerable에 대해 확장 메서드를 정의하면 이미 IEnumerable를 구현하고 있는 클래스를 수정할 필요가 없다. 이전과 동일하게 GetEnum..
C#의 제네릭을 사용하면 컴파일 타임에 타입을 미리 체크하여 에러를 사전에 잡을 수 있고, 컴파일러가 직접 타입을 캐스팅해줘서 편하고 다른 타입이지만 동일한 코드를 사용할 때, 코드 재사용성을 높여줘서 아주 유용하다. 그렇다면 왜 이렇게 편한 제네릭 인터페이스가 있는데 논제네릭 인터페이스도 같이 구현하라고 하는걸까? 논제네릭 인터페이스를 함께 구현해야 하는 이유 제네릭이 생기기 이전에 개발된 코드들을 무시할 수 있다면 좋겠지만 유감스럽게도 무시하는 것이 어려운 것이 사실이다. 새로운 라이브러리를 개발할 때 제네릭 타입뿐 아니라 고전적인 방식도 함께 지원하다면 라이브러리의 활용도를 더 높일 수 있다. 만약 제네릭 타입이 아닌 방식도 지원하겠다고 결정했다면 다음 3가지 요소에 대해서 논제네릭 방식..
제네릭을 사용하다 보면 무심코 제네릭 클래스를 만드는 게 습관이 되어버릴 수도 있다. 하지만 유틸리티 성격의 클래스를 만드는 경우에는 제네릭 클래스를 만드는 것보다 일반 클래스에 제네릭 메서드를 구현하는 것이 더 좋다. 왜 그런지 차근차근 알아보자. 왜 제네릭 메서드로?? 왜 제네릭 메서드로 구현하는 것이 좋은가? 우선, 제네릭 클래스를 작성하면 컴파일러 입장에서는 전체 클래스에 대하여 타입 매개변수에 대한 제약 조건을 고려하여 컴파일을 해야 하지만 일반 클래스 내에 제네릭 메서드들을 배치하면 각 메서드별로 제약 조건을 다르게 설정할 수 있다. 제네릭 클래스로 작성하여 제약 조건의 범위가 넓어지면 넓어질수록 코드를 수정하기가 점점 더 까다로워진다. 정리하자면, 타입 매개변수로 인스턴스 필드를 ..
제네릭 메서드를 포함한 여러 개의 오버로드된 메서드가 있는 경우, 컴파일러는 제네릭 메서드의 타입 매개변수가 다른 타입으로 다양하게 변경될 수 있음을 고려하여 오버로드된 메서드 중 하나를 선택한다. 그런데 자칫 이러한 동작 방식을 제대로 인지하지 못 할 경우, 응용프로그램이 이상하게 동작할 수도 있다. 하지만, 개발자는 컴파일러가 오버로드된 메서드 중 어떤 메서드를 선택하는지 정확히 알고 있어야 한다. 우선, 코드부터 살펴보자. public class MyBase { } public interface IMessageWriter { void WriteMessage(); } public class MyDerived : MyBase, IMessageWriter { void IMessageWriter.Wri..
언뜻 보면 C#에서 제약 조건을 설정하는 방법에는 한계가 많은 것 같다. 베이스 클래스 타입이나 특정 인터페이스로 제약 조건을 설정하거나, class 타입이나 struct 타입으로 형태를 제한하거나, 매개변수가 없는 생성자를 가져야 한다는 조건 정도를 설정하는게 끝인 것 같다. 임의의 static 메서드를 반드시 구현해야 한다거나 매개변수를 취하는 생성자를 반드시 구현하도록 제약 조건을 설정할 수는 없다. 제한적이지만 인터페이스를 통해서 제약 조건을 설정할 수는 있지만 추가적으로 해야 할 작업이 너무 많고 기본적인 구조도 해칠 수 있다. 이를 위해서 메서드의 원형에 부합하는 델리게이트를 작성하는 것이 좋다. 인터페이스를 이용한 메서드 제약 어떤 제네릭 클래스에 대해 타입 매개변수 T가 반드시 Ad..
공변성(covariance)과 반공변성(contravariance). 참 생소한 말이다. 우선 공변과 반공변에 대한 책에서 나온 정의부터 알아보자. 공변 : X를 Y로 바꾸어 사용할 수 있는 경우, C가 C를 C로 바꾸는 것이 가능하다면 공변이다. 반공변 : X를 Y로 바꾸어 사용할 수 있는 경우, C가 C를 C로 바꾸는 것이 가능하다면 반공변이다. 공변과 반공변을 처음 접해본 사람이라면 많이 혼란스러울 것 같다. 나도 그랬다. 그래서 조금 찾아봤을 때 그나마 쉽게 설명한 포스팅을 찾아봤다. 아래 링크를 참조하길 바란다. (https://edykim.com/ko/post/what-is-coercion-and-anticommunism/) 아무튼 공변성과 반공변성은 가변성(variance)..
[Effective C# Item 21] 타입 매개변수가 IDisposable을 구현한 경우를 대비하여 제네릭 클래스를 작성하라 제약 조건은 두 가지 역할을 한다. 1. 런타임 오류가 발생할 가능성이 있는 부분을 컴파일타임 오류로 돌릴 수 있다. 2. 타입 매개변수로 사용할 수 있는 타입을 명확히 규정하여 사용자에게도 도움이 된다. 대부분의 경우에 타입 매개변수로 지정하는 타입이 제약 조건을 통해 요구하는 작업 외에 다른 작업을 추가로 수행할 수 있는지에 대해선 신경 쓰지 않는다. 하지만 타입 매개변수로 지정하는 타입이 IDisposable을 구현하고 있다면 특별한 추가 작업이 반드시 필요하다. 제네릭 메서드 내에서 타입 매개변수로 주어지는 타입을 이용하여 인스턴스를 생성할 경우 publi..