[46] 리소스 정리를 위해 using과 try/finally를 활용하라

다음 본문은 도서 이펙티브 C# (빌 와그너)에서 나오는 주제를 다룹니다.

 

 

관리되지 않는 시스템 리소스를 사용하는 타입은 IDisposable 인터페이스의 Dispose()메서드를 이용하여 명시적으로 리소스를 해제해야 한다. 사용자 입장에서 Dispose()메서드가 항상 호출되도록 코드를 작성하기 위한 최선의 방법은 using문이나 try/finally 블록을 활용하는 것이다.

 

using 문 사용하기

다음과 같은 코드를 작성했다고 해보자.

SqlConnection과 SqlCommand는 둘 다 Dispose()를 구현한 객체지만 사용자가 Dispose()를 호출하지 않았기 때문에 finalizer가 호출될 때까지 메모리에 남게 된다.

public void ExecuteCommand(string connString, string commandString)
{
    SqlConnection myConnection = new SqlConnection(connString);
    var mySqlCommand = new SqlCommand(commandString, myConnection);
    myConnection.Open();
    mySqlCommand.ExecuteNonQuery();
}

그렇다면 직접호출하면 해결될까?

public void ExecuteCommand(string connString, string commandString)
{
    var myConnection = new SqlConnection(connString);
    var mySqlCommand = new SqlCommand(commandString, myConnection);
    myConnection.Open();
    mySqlCommand.ExecuteNonQuery();
    
    mySqlCommand.Dispose();
    myConnection.Dispose();
}

하지만 이건 sql 명령 실행 중 어떠한 예외도 발생하지 않았을 때 유효하다.

 

때문에 이 경우 using 문 내에서 객체를 할당하면 C# 컴파일러가 해당 객체에 대해 try/finally 블록을 자동으로 생성하여 항상 Dispose()가 호출되도록 해준다.

public void ExecuteCommand(string connString, string commandString)
{
    using(SqlConnection myConnection = new SqlConnection(connString))
    {
        using(SqlCommand mySqlCommand = new SqlCommand(commandString, myConnection))
        {
            myConnection.Open();
            mySqlCommand.ExecuteNonQuery();
        }
    }
}

 

using문 사용하기

단, IDisposable 인터페이스를 지원하지 않는 타입에 대해서 using문을 사용하면 컴파일 에러가 발생한다.

obj는 IDisposable인터페이스를 지원하지 않기때문에 아래의 코드는 컴파일되지 않는다.

using(object obj = Factory.CreateResource())
    Console.WriteLine(obj.ToString());

이 경우 as를 이용하면 타입이 IDisposable을 실제로 구현했는지와 관계없이 안전하게 코드를 작성할 수 있다.

object obj = Factory.CreateResource();
using(obj as IDisposable)
    Console.WriteLine(obj.ToString());

 

중첩된 using문 피하기

중첩된 uinsg을 피하기 위해 아래와 같이 명시적으로 try/finally문을 구성하는 방법도 있다.

public void ExecuteCommand(string connString, string commandString)
{
    SqlConnection myConnection = null;
    sqlCommand mySqlCommand = null;
    
    try
    {
         myConnection = new SqlConnection(connString);
         mySqlCommand = new SqlCommand(commandString, myConnection);
         myConnection.Open();
         mySqlCommand.ExecuteNonQuery();
    }
    finally
    {
        if(mySqlCommand != null)
            mySqlCommand.Dispose();
        if(myConnection != null)
            myConnection.Dispose();
    }
}

 

결론

 IDisposable을 구현할 타입을 사용할 때는 어떤 경우라도 올바르게 Dispose()메서드가 호출될 수 있도록 코드를 작성해야 한다.

 

 

댓글

Designed by JB FACTORY