[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()..
[Effective C# Item 12] 할당 구문보다 멤버 초기화 구문이 좋다 C# 코딩을 하다보면 클래스를 생성할 때 생성자를 2개 이상 작성해야 할 경우도 있다. 이 때 생성자 내에서 멤버 변수들의 값을 초기화하도록 코딩하다 보면 모든 생성자에서 초기화를 해줘야 하는데 자칫 멤버 변수 초기화 코드가 누락될 수 있다. 이런 실수를 하지 않으려면 멤버 초기화 구문(member initializer)를 사용하는 것이 좋다. 멤버 초기화 구문(member initializer) public class MyClass { // 컬렉션을 선언하는 동시에 초기화 private List labels = new List(); } 위 예제가 바로 멤버 초기화 구문이다. 위와 같이 코딩을 하면 MyClass 타입..
[Effective C# Item 11] .NET 리소스 관리에 대한 이해 .NET 의 가비지 콜렉터의 동작 방식을 잘 알아야 하고, 비관리 리소스를 어떻게 다룰지에 대해서도 정확히 이해해야 한다. 가비지 콜렉터 가비지 콜렉터는 관리되는 메모리를 관장하며 네이티브 환경과는 다르게 메모리 누수, 댕글링 포인터, 초기화되지 않는 포인터, 다른 메모리 관리 문제를 개발자들이 직접 다루지 않도록 자동화해준다. 가비지 콜렉터가 메모리 전반적으로 관리해주기 때문에 개발자는 응용프로그램의 구조를 단순하게 유지할 수 있다. 가비지 콜렉터의 마크/콤팩트(Mark/Compact) 알고리즘은 여러 객체 사이의 연관 관계를 효율적으로 파악하여 더 이상 사용되지 않는 객체를 자동으로 제거한다. 가비지 콜렉터가 수행되..
[Effective C# Item 10] 베이스 클래스가 업그레이드된 경우에만 new 한정자를 사용하라 베이스 클래스에서 virtual로 선언하지 않은 멤버를 재정의하려는 경우에 new 한정자를 사용해서 재정의할 수 있다. 허나 사용할 수 있는 것과 잘 사용하는 것은 전혀 다른 이야기다. 모든 프로그래밍에서 그렇듯 안 될 거 같은 것을 억지로 바꾸려할 경우에는 사용하는 데 있어서 신중해야 한다. 앞의 Item 9의 박싱과 언박싱도 그 예중 하나다. virtual로 선언되지 않은 메서드를 new 한정자로 억지로 재정의하면 메서드의 동작 방식을 모호하게 만들 우려가 있기 때문이다. 엉? 왜 동작이 다른거야? object c = MakeObject(); // MyClass 타입의 참조를 이용하여 메서드..
[Effective C# Item 9] 박싱과 언박싱을 최소화하라 Value 타입은 주로 값을 저장할 때 쓰는 저장소이며 다형적이지 못하다. 그리고 System.Object는 .NET 프레임워크에서 모든 타입의 최상위 타입으로 정의하고 있는데 언뜻 보면 Value 타입과 System.Object는 양립하지 못하는 것처럼 보인다. 하지만 .NET 프레임워크는 박싱과 언박싱이라는 방법을 통해서 두 타입을 서로 변환하게 해준다. 박싱과 언박싱(Boxing and Unboxing) 박싱 : Value 타입의 객체를 타입이 정해져 있지 않은 임의의 참조 타입 내부에 포함시키는 방법 언박싱 : 박싱되어 있는 참조(reference) 타입의 객체로부터 Value 타입 객체의 복사본을 가져오는 방법 이런 박..