제네릭 메서드를 포함한 여러 개의 오버로드된 메서드가 있는 경우, 컴파일러는 제네릭 메서드의 타입 매개변수가 다른 타입으로 다양하게 변경될 수 있음을 고려하여 오버로드된 메서드 중 하나를 선택한다. 그런데 자칫 이러한 동작 방식을 제대로 인지하지 못 할 경우, 응용프로그램이 이상하게 동작할 수도 있다. 하지만, 개발자는 컴파일러가 오버로드된 메서드 중 어떤 메서드를 선택하는지 정확히 알고 있어야 한다. 우선, 코드부터 살펴보자. 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..
[Effective C# Item 20] IComparable와 IComparer를 이용하여 객체의 선후 관계를 정의하라 컬렉션을 정의하거나 검색하려면 타입 내에 선후 관계를 판단할 수 있는 기능을 정의해야 한다. 이를 위해서 .NET 프레임워크는 객체의 선후 관계를 정의하는 인터페이스인 IComparable와 IComparer를 제공한다. IComparable를 이용하여 타입의 기본적인 선후 관계를 정의하고 IComparer를 이용하여 기본적인 선후 관계 이외에 추가적인 선후 관계를 정의할 수 있다. 이번 항목에서 우리가 배울 것! 1. 객체의 선후 관계를 구현하는 방법 2. 인터페이스를 이용하여 .NET 프레임워크가 객체의 순서를 정렬하는 원리 그래서 만약 어떤 알고리즘이 특정 타입에 ..
[Effective C# Item 19] 런타임에 타입을 확인하여 최적의 알고리즘을 사용하라 제네릭을 활용하면 코드를 덜 작성해도 되기 때문에 매우 유용하지만 타입이나 메서드를 제네릭화하면 구체적인 타입이 주는 장점을 잃고 타입의 세부적인 특징을 고려한 최적화한 알고리즘도 사용할 수 없다. 그래서 만약 어떤 알고리즘이 특정 타입에 대해 더 효율적으로 동작한다고 생각된다면 그냥 그 타입을 이용하도록 작성하는 것도 좋다. 직전 Item 때 처럼 제약 조건을 설정하는 방법도 있지만 제약 조건이 항상 능사는 아니다. 특정 타입의 시퀀스를 역순으로 순회하기 위해서 다음과 같이 클래스를 만들었다. public sealed class ReverseEnumerable : IEnumerable { private ..
[Effective C# Item 18] 반드시 필요한 제약 조건만 설정하라 타입 매개변수에 대한 제약 조건(constraint)은 클래스가 적업을 올바르게 수행하기 위해서 타입 매개변수로 전달할 수 있는 타입의 유형을 제한하는 방법이다. 올바른 작업을 수행하기 위한 제약 조건의 수가 늘어나면 개발자가 이를 만족시키기 위해서 수행해야 하는 작업의 양이 늘어나서 이 균형을 유지하는 것이 중요하다. 제약 조건을 설정하지 않으면 런타임에 더 많은 검사를 수행하고 더 자주 형변환을 해야 하고, 리플렉션을 사용해야 할 가능성이 커진다. 제약 조건을 과도하게 설정하면 해당 클래스를 사용하기 위해 과도하게 추가 작업을 해야 한다. 제약 조건 왜 사용하나? 제약 조건을 설정하면 컴파일러는 System.Obj..
[Effective C# Item 17] 표준 Dispose 패턴을 구현하라 이번 포스팅은 메모리가 아닌 다른 유형의 비관리(Unmanaged) 리소스를 포함한는 타입을 작성할 때 리소스 관리를 어떻게 해야 할지를 살펴보는 파트다. 우선 간단하게 비관리 리소스가 무엇인지 살펴보자. 비관리 리소스 : 메모리가 아닌 자원을 말하며, 윈도우 핸들, 파일 핸들, 소켓 핸들 등 시스템 자원을 뜻한다. 반대로 관리 리소스에는 new List() 등, 메모리처럼 쓰는 자원을 말한다. 이런 비관리 리소스들은 가비지 콜렉터가 아닌 개발자가 직접 관리해줘야 한다. 이미 .NET 프레임워크에는 비관리 리소스를 정리하는 표준화된 패턴을 사용하고 있다. 그것이 바로 Dispose 패턴이다. Dispose 패턴..