목록C#/Effective C# (50)
방프리

Item 18: 반드시 필요한 제약 조건만 설정하라 이 주제에 앞서 제약 조건이란? 클래스가 작업을 올바르게 수행하기 위해서 타입 매개변수로 전달할 수 있는 타입의 유형을 제한하는 방법이다. 제약 조건을 적절히 잘 사용하면 예외에 대한 처리를 쉽게 할 수 있으나 너무 설정하지 않으면 런타임에 들어가는 예외처리가 많아지고 그렇다고 너무 조건이 있으면 해당 클래스를 사용 하기 위해 추가작업이 많이 들어가게 된다. 이 제약 조건은 제네릭 타입에 대해 우리가 가정하고 있는 사실을 컴파일러와 다른 개발자에게 알려주는 용도로 사용된다. 컴파일러에게 제약 조건을 알려주는 것은 컴파일러 입장에서 두 가지 장점이 있는데 첫 번째는 제네릭 타입을 작성할 때, 두 번째는 제네릭 타입을 사용하는 하용자가 타입 매개변수로 올바..

Item 17: 표준 Dispose 패턴을 구현하라 C++에서 코딩할 때 가장 중요한 것은 메모리 관리이다. 솔직히 DirectX나 학교 과제로 MFC 공부할 때, 특히 서버 개발 일을 할 때 메모리 관리하는 것은 굉장히 신경쓰이고 짜증나는 일이었다. C#에서는 가비지 컬렉터가 알아서 처리해주니까 이에 대한 부담감이 확실히 덜하긴 하지만 내부적으로 봤을 때 가비지 컬렉터도 코드로 돌아가고 있는 존재이기 때문에 마냥 안심만은 할 수가 없다. 또한 메모리라는 것이 가비지컬렉터가 알아서 해주면 편하지만 이 시점을 개발자들이 제대로 알 수 없기 때문에 버그를 만들 수도 있다. 예로 언리얼 엔진 서밋에 참가했을 때 맨 처음 C#으로 개발을 진행했던 프로젝트가 메모리가 해제되는 시점을 제대로 알 수 없어서 C++로..

Item 16: 생성자 내에서는 절대로 가상 함수를 호출하지 말라 C++과 C#에서 생성자가 동작하는 방식의 기준은 많이 다르다고 한다. 그로 인해 개발자들이 예상하는 값과는 전혀 다른 형태의 값을 출력할 때도 있다. 다음의 코드를 분석하면 C++ 개발자들은 보통 "VFunc in B" 이 출력될 것이라 생각하지만 C#은 생성자 구문에 인스턴스가 들어오면 초기화가 끝났다고 판명하기 때문에 선언과 동시에 초기화를 해준 "Set by initializer"가 마지막으로 출력이 된다. C++은 가상함수가 생성 중인 객체의 타입을 확인하도록 되어있으나, C#은 다르다. 또한 C#은 현재 타입이 추상 베이스 클래스인 경우 가상 메서드가 null 메서드 포인터가 될 가능성을 배제하였다. C#의 컴파일러는 런타입 때..

Item 15: 불필요한 객체를 만들지 마라 새로운 객체를 만드는 것은 굉장히 오랜 시간을 필요로 한다. 때문에 자주 사용하는 객체는 Pool이라는 개념을 사용하여 미리 생성한 후 재사용하는 방식을 많이 채택하게 되는데 이번 항목의 주제와 비슷하다. Pool 개념을 가지고 다음의 코드를 보면 어떤 점이 문제점인지 바로 인지할 수 있다. 기본적으로 Paint라는 이름이 들어가는 함수들은 매 프레임마다 호출해주어야 하는 자주 사용되는 함수다. 가끔 사용되는 함수일 경우엔 몰라도 계속 출력되어야 하는 함수에 객체를 저렇게 생성해준다면? 유지보수가 지속될수록 퍼포먼스가 굉장히 떨어질 것이다. 이럴 경우엔 저자는 다음과 같이 수정을 권한다. 자주 사용되는 객체라면 차라리 지역변수가 아닌 멤버변수로 바꾸어 재사용하..

Item 14: 초기화 코드가 중복되는 것을 최소화하라 C++ 개발자들은 여러 생성자 구문이 있다면 공통적인 부분을 private 헬퍼를 만든 후 사용한다. 하지만 저자는 이 방법은 C#에서는 그다지 좋은 방법이 아니라고 한다. 위와 같이 헬퍼 메서드를 작성하는 것보다는 공용으로 사용할 수 있는 생성자를 만드는 것을 더 권장했다. 아마 컴파일러의 인식의 차이점 때문에 그런 듯 싶다. C# 4.0에서 추가된 기본 매개 변수 기능을 활용하면 위의 코드를 다음과 같이 더 줄일 수 있다. 여러 개의 생성자를 생성하는 것보단 기본으로 주는 생성자 하나를 구현하는 방식이 훨씬 더 좋다. 하지만 이에 따른 트레이드 오프는 항상 고려해보아야 한다. 다음과 같이 구조를 고려해 볼 수 있으나 이 코드의 경우엔 내부적으로 ..

Item 13: 정적 클래스 멤버를 올바르게 초기화하라. 정적 클래스는 주로 Singleton 패턴을 구현할 때 많이 사용한다. Unity에서도 GameManager나 SoundManager 등 각종 매니저 클래스를 구현할 때 Singleton 패턴을 많이 사용한다. 이렇게 자주 쓰이는 정적 클래스도 초기화를 어떻게 해주느냐에 따라 더욱 효과적으로 사용할 수 있다. 정적 클래스도 일반 클래스와 동일하게 초기화를 잘 해주어야 하는데 동작 순서는 일반 클래스와 동일하게 초기화 구문이 실행된 후 정적 클래스의 생성자 구문이 실행된다. 일반 클래스와 다른 점은 이 과정이 일반 클래스보다 더 빨리 호출된다는 점이다. 초기화 과정이 복잡하다면 일반 클래스와 똑같이 생성자를 적극적으로 활용하는 것도 좋은 방법이다. ..

Item 12: 할당 구문보다 멤버 초기화 구문이 좋다. 변수를 선언한 시점에서 C#에서는 데이터를 기본값으로 초기화 시켜준다. 예를 들어 int 타입의 경우엔 0이 들어가고 사용자 정의 클래스의 경우엔 null이 자동으로 할당된다. C++에서 항상 해주어야하는 것처럼 선언과 동시에 초기화를 해주는 것은 C#에서도 굉장히 중요하다. 대신 기본적인 데이터(null, 0, "" 등)의 경우엔 컴파일러가 알아서 해주니 굳이 작성할 필요가 없다. 또한 List 같은 객체의 생성이 혼잡할 경우엔 미리 선언해주는 것은 되도록이면 피하는 것이 좋다. 또한 메모리를 할당할 때에 예외처리 구문이 필요하다면 초기화 구문보다는 생성자를 적극적으로 이용하는 것이 좋다. try catch나 다른 조건문 처리가 가능하며 한정적인..

Item 11: .NET 리소스 관리에 대한 이해 프로그래밍을 공부하면서 제일 이해하기 힘들고, 제일 찾기 힘든 버그는 당연코 메모리 관리라고 할 수 있다. C, C++에서는 하드웨어에 직접적으로 접근하는 대신 메모리 관리에 굉장히 엄격한데 C#은 GC를 통해서 이에 대한 부담을 조금 덜어준다. GC는 내부적으로 메모리를 관리해주면서 오랫동안 사용되지 않은 객체를 자동으로 해제하며, 사용되는 객체도 정돈을 하여 가용 메모리 공간을 좀 더 확장을 시킬 수도 있다. 하지만 이 때문에 메모리 해제 부분의 규칙성을 침해받을 수도 있는지 C# 가이드 라인에서도 소멸자는 되도록이면 호출하지 않는 것이 좋다고 하는데 저자 또한 finalizer와 IDisposable 키워드를 통해 메모리를 수동으로 관리하되 사용 시..