본문 바로가기
책/Effective C#

Effective C# - Item12 할당 구문보다 멤버 초기화 구문이 좋다

by 코딩하는 돼징 2023. 11. 30.
반응형

클래스를 만들다보면 종종 둘 이상의 생성자를 작성해야 하는 경우가 있다. 그러면 자칫 초기화 코드를 누락하는 경우가 있는데 이러한 오류를 범하지 않으려면 생성자의 본문에서 멤버 변수에 값을 할당하기보다 멤버 초기화 구문을 사용하는 것이 좋다.

 

멤버 초기화 구문

생성자에게 멤버 변수에 값을 할당하는 대신에 해당 변수를 선언하는 동시에 초기값을 설정하는 방법이다.

 

01 멤버 초기화 구문의 사용

public class MyClass
{
    //컬렉션을 선언하는 동시에 초기화
    private List<string> labels = new List<string>();
}

 

02 멤버 초기화 구문과 생성자

멤버 초기화 구문에서 변수를 초기화하면 생성자 본문에서 해당 변수를 초기화할 필요가 없다. 멤버 초기화 구문이 생성자보다 먼저 실행되므로 생성자에서 멤버 변수를 초기화하는 코드가 없어도 해당 변수는 이미 초기화 되어있다.

03 상속과 초기화 순서

클래스가 다른 클래스를 상속하고 있다면 상속된 클래스의 생성자는 파생 클래스의 생성자보다 먼저 호출된다. 멤버 초기화 구문은 생성자 본문의 앞쪽에 위치하므로 파생 클래스의 생성자에서 상속된 클래스의 멤버 변수를 초기화하기 전에 해당 변수가 초기화 된다.


멤버 초기화 구문의 한계

다음 세가지 경우 멤버 초기화 구문을 사용하지 않는 것이 좋다.

01 객체를 0이나 null로 초기화 하는 경우

기본 시스템 초기화 루틴은 코드를 실행하기 전에 모든 값을 0으로 설정한다. 이는 시스템 초기화 루틴은 저수준에서 직접 CPU명령을 수행하여 메모리 블록을 0으로 설정하기 때문에 추가적으로 변수의 값을 0이나 null로 설정할 필요가 없다.

public struct MyValType
{
    // 생략
}
MyValType myVal1; //0으로 초기화
MyValType myVal2 = newValType(); // 반복해서 0으로 초기화

02 동일한 객체를 반복해서 초기화 하는 경우

만약 클래스가 여러 생성자를 가지고 있고 그 중 일부에서 동일한 객체를 다양하게 초기화한다면 멤버 초기화 구문을 사용하지 않는 것이 좋다. 멤버 초기화 구문은 모든 생성자에서 동일한 초기화를 수행할 때 유용하며 다양한 초기화 방법이 혼재할 경우 코드의 가독성을 떨어뜨릴 수 있다.

public class MyClass2
{
    //컬렉션을 선언하는 동시에 초기화
    private List<string> labels = new List<string>();
    
    MyClass2()
    {
    }
    
    MyClass2(int size)
    {
        labels = new List<string>(size);
    }
}

컴파일러가 실제로 생성하는 코드는 다음과 유사하다. 우리가 직접 작성하지 않았지만 불필요한 코드가 포함된다.

첫번째 생성자에서 이미 초기화된 labels객체를 가비지 컬렉션의 대상으로 만들어 메모리가 낭비되는 것을 볼 수 있다. 두번째 생성자의 첫번째라인에서 생성한 빈 리스트는 사용되지 않고 가비지 컬렉션이 된다.

public class MyClass2
{
    //컬렉션을 선언하는 동시에 초기화
    private List<string> labels = new List<string>();
    
    MyClass2()
    {
        labels = new List<string>();
    }
    
    MyClass2(int size)
    {
        labels = new List<string>();
        labels = new List<string>(size);
    }
}

03 예외 처리가 반드시 필요한 경우

멤버 초기화 구문은 try로 감쌀 수 없기 때문에 초기화 과정에서 예외가 발생하면 예외가 외부로 전파된다. 이 경우, 예외 처리가 반드시 필요하다면 멤버 초기화 구문 대신 생성자 내부로 초기화 코드를 옮기고 예외 처리 로직을 적절히 구현해야 한다. 멤버 초기화 구문으로는 내부적인 예외 처리가 어려우므로 해당 경우에는 생성자 내부에서 초기화 및 예외 처리를 수행해야 한다.


결론

할당 구문보다 초기화 구문이 좋다. 모든 생성자가 동일한 방법으로 멤버 변수를 초기화하는 경우에 한해서만 이 방법을 사용해야 한다.

 

 

 

 

본 게시글은 Effective C#을 읽고 정리하였습니다.

 

 

반응형

댓글