본문 바로가기

프로그래밍&IT/C# & Winfrom

[Effective C#] Ch2. .Net 리소스 관리 (1) 초기화 관련되어서, 리소스 해제

728x90
반응형

12. 생성자에서 필드를 초기화하지 말고, 필드 선언부에서 바로 초기화 하라.

public class Customer
{
    private List<Order> _orders;

    public Customer()
    {
        _orders = new List<Order>();  // 생성자에서 초기화
    }
}

// 추천
public class Customer
{
    private List<Order> _orders = new List<Order>();  // 선언과 동시에 초기화
    public string Name { get; set; } = "Unknown";
}

 

이유 설명
코드 간결성 생성자에 중복된 초기화 코드 줄어듦
가독성 향상 각 필드가 어떤 기본값을 갖는지 바로 보임
일관성 유지 모든 생성자에서 누락 없이 동일한 값으로 초기화됨
생성자 오버로드 시 편리 여러 생성자에 초기화 중복을 피할 수 있음

 

13. 정직 필드/속성은 선언시점에 초기화하거나 정적 생성자 (static constructor)를 명확히 활용하라.

초기화 순서를 잘못다루면 NullReferenceException, 예기치 모한 동작, 타입 초기화 순환 문제 등이 발생할 수 있다.

이유 설명
정확한 초기화 순서 보장 static 생성자나 선언 초기화는 .NET이 타입 로드 시점에 딱 한 번 호출
스레드 안전성 확보 정적 생성자는 CLR이 자동으로 동기화 처리
명확한 책임 분리 초기화 코드가 흩어지지 않고 명확히 관리됨
불필요한 오버헤드 방지 잘못된 지연 초기화나 복잡한 조건식이 줄어듦

 

주의할 점

정적 생성자는 오직 한 번만 호출됨 여러 번 호출될 것처럼 착각하면 안 됨
타입 초기화 순환 주의 A 타입의 static 멤버가 B 타입을 참조하고, B 타입 static이 다시 A를 참조하면 예외 발생 가능
예외 발생 시 타입 로드 실패 static 생성자에서 예외가 발생하면, 해당 타입은 앱 도는 동안 다시 사용할 수 없음 (TypeInitializationException)

 

14. 중복된 초기화 로직을 여러번 작성하지 말고 한 곳에만 두도록 하라

// 잘못된 예. Age가 여러 곳에서 초기화 되고 있다.
public class User
{
    public string Name;
    public int Age;

    public User()
    {
        Name = "Unknown";
        Age = 0;
    }
    public User(string name)
    {
        Name = name;
        Age = 0;
    }
    public User(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

//개선된 예
public class User
{
    public string Name;
    public int Age;

    public User() : this("Unknown", 0) { }

    public User(string name) : this(name, 0) { }

    public User(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

 

15. 리소스를 정리할 때는 using 문이나 try / finally 블록을 활용하라.

여기서 얘기하는 리소스는?

- FileStream, SqlConnection, StreamReader 등 메모리 외부 자원 (파일, 네트워크, DB 등)

  • 이런 리소스들은 사용후 반드시 해제 (cleanup)을 해줘야 리소스 누수를 방지할 수 있다.
  • 이에 자동 또는 확실하게 해제할 수 있도록 using 이나 try / finally를 사용하라
using (var reader = new StreamReader("file.txt"))
{
    string content = reader.ReadToEnd();
}

StreamReader reader = null;
try
{
    reader = new StreamReader("file.txt");
    string content = reader.ReadToEnd();
}
finally
{
    if (reader != null)
        reader.Dispose();
}

try 블록에서 예외가 발생해도 finally는 반드시 실행되므로 안전하게 리소스를 해제할 수 있다.

상황 권장 방식
일반적인 IDisposable 리소스 사용 using 문
리소스를 조건적으로 열거나 복잡한 흐름 제어 필요 try/finally