개발/C#

[Effective C#]C# 멤버 초기화 - 할당 구문보다 멤버 초기화를 사용한다.

huiyu 2020. 12. 30. 06:00

할당 구문보다 멤버 초기화(member initializer) 구문 사용

- 생성자가 여러개인 경우, 멤버 변수 초기화를 누락하여 사용할 수 있다. 멤버초기화는 이러한 누락을 방지할 수 있다.

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

이와 같은 코드는 MyClass 타입에 몇개의 생성자가 추가되든 올바른 초기화를 사용할 수 있다.

* 컴파일러는 모든 생성자의 시작 부분에 멤버 초기화 구문을 포함시킨다. 새로운 생성자가 추가되거나 사용자가 생성자를 선언하지 않아 기본 생성자가 만들어진 경우에도, 항상 생성자가 실행되기 전 멤버 초기화 구문이 실행된다.

멤버 변수 초기화(member initializer) 특징

- 모든 생성자의 시작 부분에 멤버변수 초기화
- 상속받은 클래스의 경우, 베이스 클래스의 생성자 초기화 전 진행
- 멤버 변수 초기화 순서는 선언 순서대로 수행

멤버변수 초기화 사용시 주의사항

1) 0이나 null로 초기화 하는 경우
 시스템 초기화 루틴은 코드를 실행하기 전 모든 값을 0으로 설정한다. 추가적으로 0이나 null로 설정한 경우 불필요한 작업을 한번 더 수행하게 되므로 추가시간이 소요되게 된다.

1
2
3
4
5
6
public struct MyValType
{
}
//생략
MyValType myVal1;
MyValType myVal2 = new MyValType(); // 반복해서 0으로 초기화
 
cs

첫 번째 문장은 myVal1이 사상된 블록을 모두 0으로 설정한다. 두 번째 문장은 initObj라는 IL 명령을 사용하는데 박싱/언박싱된 myVal2변수 모두에 대해서 0으로 초기화하는 과정이 수행된다. 이과정으로 약간의 추가 시간이 소요된다.
 * initObj -> link

2) 동일한 객체 반복 초기화
 멤버 초기화 구문은 객체 생성 방법이 모든 생성자에서 동일한 경우에 사용하는 것이 좋다. 만약 아래 MyClass2와 같이 List 객체를 생성하는 방식이 다양할 경우 멤버 초기화 구문을 사용하지 않는 것이 좋다.

1
2
3
4
5
6
7
8
9
10
11
12
public class MyClass2
{
  // 컬렉션을 선언하는 동시에 초기화
  private List<string> labels = new List<string>();
 
  MyClass2() {    }
 
  MyClass2(int size) 
  {
    labels = new List<string>(size);
  }
}
cs

위 예제에서 멤버변수에서 초기화를 진행하며, MyClass2 생성자에서도 컬렉션의 크기를 지정하는 초기화를 진행하고 있다. 이렇게 되면 실제로 2개의 List<>객체가 생성된다.
(첫번째 생성 -> 멤버초기화, 2번째 생성 -> 생성자. 생성자에서 생성되는 객체만 살아남게 된다.)
-> 결국, 불필요한 초기화를 두번 진행하게 된다.

3) 예외처리가 반드시 필요한 경우
 - 멤버 초기화 구문은 try로 감쌀 수 없다. try로 예외를 감쌀 수 없기 때문에 초기화 과정 예외가 발생하면 예외는 외부로 전파된다. 따라서 클래스 내부에서 복구를 시도할 수 없다. 반드시 예외 처리가 필요하면 생성자 내부로 초기화 코드를 옮기고 예외처리를 구현해야 한다.

*** 멤버 초기화 구문은 생성자가 여러 개일 경우에도 멤버 변수 초기화를 누락하지 않게 도와준다. 향후 새로운 생성자가 추가되는 경우에도 멤버 변수에 대한 초기화를 누락하는 상황을 방지할 수 있다. 다만 모든 생성자가 동일한 방법으로 멤버 변수를 초기화 하는 경우에 한해서만 이 방법을 사용해야 한다.

** 멤버 초기화 구문을 사용하면 코드를 읽기도 쉽고 유지보수도 용이하다.

[참고]
Effective C# - 아이템12 : 할당 구문보다 멤버 초기화 구문이 좋다

728x90
반응형