[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 패턴..
[Effective C# Item 16] 생성자 내에서는 절대로 가상 함수를 호출하지 마라 객체가 완전히 생성될 때 까지는 가상 함수를 호출하면 이상 동작을 일으킨다. 어떤 타입이든 생성자가 수행을 완료하기 전에는 객체가 완전히 생성되었다고 할 수 없다. 따라서 생성자 내에서 가상 함수를 호출하면 개발자의 예상과는 다르게 코드가 흘러갈 수 있다. class B { protected B() { VFunc(); } protected virtual void VFunc() { Console.WriteLine("VFunc in B"); } } class Derived : B { private readonly string msg = "Set by Initializer"; public Derived(string ..
[Effective C# Item 15] 불필요한 객체를 만들지 말라 가비지 콜렉터는 개발자를 대신해서 메모리를 훌륭히 관리하며 사용하지 않는 객체를 효율적으로 제거한다. 하지만 힙에서 새로운 객체를 생성하고 삭제하는 작업은 생각보다 많은 프로세서 시간을 사용하고 너무 많은 객체를 생성하면 심각한 성능 문제를 초래할 수 있다. 모든 참조 타입의 객체는 지역 변수라도 동적으로 메모리를 할당하는데 이렇게 할당된 객체는 이 객체를 참조하는 상위 멤버가 삭제되면 가비지가 된다. 참조 타입의 지역 변수를 멤버 변수로 선언 가장 흔히 저지르는 나쁜 예 중 하나로 매우 자주 호출되는 이벤트 핸들러에 참조 타입의 객체를 지역 변수로 선언하면 가비지 콜렉터가 생성, 제거를 많이 반복하여 성능에 무리를 준다. ..
[Effective C# Item 14] 초기화 코드가 중복되는 것을 최소화해라 코딩을 하다가 한 클래스에 생성자를 여러개 작성할 때 종종 동일한 작업을 되풀이할 때가 있어서 기존에 작성한 코드를 그대로 복사하고는 한다. 하지만 클린 코드 관점에서 중복 코드는 좋지 않은 코드이기 때문에 이 경우엔 공용으로 사용할 수 있는 생성자를 작성하는 편이 낫다. 다른 생성자를 호출하여 초기화 과정의 일부를 위임 공통으로 사용하는 생성자를 다른 생성자에서 호출하여 변수에 대한 중복 초기화 코드를 제거해 줄 뿐 아니라 베이스 클래스의 생성자가 반복적으로 호출되는 것도 막아준다. public class MyClass { // 데이터 컬렉션 private List coll; // 인스턴스 변수 private s..
[Effective C# Item 13] 정적 클래스 멤버를 올바르게 초기화하라 정적 멤버를 포함하는 타입이 있다면 인스턴스를 생성하기 전에 반드시 정적 멤버 변수를 초기화해줘야 한다. 정적 멤버를 초기화하기 위해 C#은 정적 생성자와 정적 멤버 초기화 구문을 제공한다. 1. 정적 생성자 정적 생성자는 타입 내에 정의된 모든 메서드, 변수, 속성에 최초로 접근하기 전에 호출되는 특이한 메서드다. 이 메서드를 활용하여 정적 변수를 초기화하거나, 싱글턴 패턴을 적용해보거나, 생성자 호출 전이든 다양한 작업을 하기 편하다. public class MySingleton1 { private static readonly MySingleton1 theOneAndOnly; static MySingleton1()..