C#과 IDisposable 인터페이스

System.IDisposable 인터페이스는 일반적으로 재정의하는 클래스들 가운데서 메모리 할당 및 소거 비용이 비싼 클래스를 위하여 특별히 제공되는 인터페이스입니다. 일반적으로 System.ValueType으로부터 상속받는 System.Int16, System.Int32와 같이 가볍고 안전한 타입이거나 비교적 적은 크기의 인스턴스라면 꼭 IDisposable 인터페이스를 구현하지 않는다 할지라도 가비지 컬렉터에 의해서 자동으로 수거되도록 내버려 두어도 문제가 되지 않습니다.


그와는 달리 System.Net.Sockets.Socket과 같은 System-Wide 클래스 혹은 System.Windows.Forms.Form과 같이 많은 수의 인스턴스를 복합적으로 참조하는 클래스들은 “필요할 때에만” 사용하고 즉시 정리해주는 것이 좋습니다. 동시에 많은 처리를 해야 하는 서버 응용프로그램의 경우 가비지 컬렉터가 이 개체를 수집하도록 내버려둔다면 여러번 반복적으로 이런 개체가 생성될 것이고 생각하지 못한 예외 사항이나 성능 저하가 발생할 소지가 있기 때문입니다. (예를 들었던 두 클래스의 경우 “꽤 긴 시간동안” 사용되므로 어떤 메서드 안에서 잠깐 사용되고 말 비싼 개체의 범주에는 포함되지 않습니다.)


모든 언어에 관계없이 다음과 같은 형태로 어떤 메서드 안에서 잠깐 사용하고 말 비싼 개체를 손쉽게 이용할 수 있습니다. 여기서는 C#과 VB.NET의 코드를 예로 들어보도록 하겠습니다.



    // C# 코드


    void OpenDatabase(string providerName, string dataSource)


    {


    if(providerName.Length <= 0 || dataSource.Length <= 0)


     throw new ArgumentException(“Do not provide empty string.”);


    string fullOption = String.Format(new CultureInfo(String.Empty), “Provider={0};Data Source={1};”, providerName, dataSource);


    OleDbConnection conn = new OleDbConnection(fullOption);


    try


    {


     conn.Open();


     // 필요한 작업은 여기서 모두 수행합니다.


    }


    finally


    {


     IDisposable disp = conn as IDisposable;


     if(disp != null) disp.Dispose();


    }


    }

    ‘ VB.NET 코드


    Sub OpenDatabase(ByVal ProviderName As String, ByVal DataSource As String)


    If ProviderName.Length <= 0 Or DataSource.Length <= 0 Then


     Throw New ArgumentException(“Do not provide empty string.”);


    End If


    Dim FullOption As String = String.Format(“Provider={0};Data Source={1}”, ProviderName, DataSource)


    Dim Connection As New OleDbConnection(FullOption);


    Try


     Connection.Open()


     ‘ 필요한 작업은 여기서 모두 수행합니다.


    Finally


     Dim disp As IDisposable


     If TypeOf Connection Is IDisposable Then


      disp = Connection


      disp.Dispose()


     End If


    End Try


    End Sub


위에서 작성된 코드에서 Try/Finally 블럭을 잘 보세요. 개체를 생성한 뒤 진입하게 된 블럭 안에서 우선은 필요한 작업을 수행하게 됩니다. 부가적으로 Catch 블럭을 이용하여 예외를 잡아낼 수도 있겠습니다만 번외의 이야기이므로 이곳에 집중합니다. Finally 블럭은 예외가 발생했든 발생하지 않았든 무조건 도착하는 영역입니다. 이곳에서 정리 작업을 수행하는데, IDisposable 인터페이스와 호환성이 있는 클래스인지 검사한 뒤 호환성이 있다면 직접 형식 변환을 수행해서 IDisposable.Dispose 메서드를 호출합니다. 이것은 다시 OleDbConnection.Dispose 메서드를 호출하게 되어있습니다.


모든 언어는 위와 같이 Try/Finally 블럭으로 IDisposable의 기능을 사용할 수 있습니다. 그러나 C#은 이와 같이 복잡한 코드를 사용하지 않아도 됩니다. 바로 using 키워드가 그 해답인데, using 키워드를 사용하는 OpenDatabase 메서드를 살펴보겠습니다.



    void OpenDatabase(string providerName, string dataSource)


    {


    if(providerName.Length <= 0 || dataSource.Length <= 0)


     throw new ArgumentException(“Do not provide empty string.”);


    string fullOption = String.Format(new CultureInfo(String.Empty), “Provider={0};Data Source={1};”, providerName, dataSource);


    using(OleDbConnection conn = new OleDbConnection(fullOption))


    {


     conn.Open();


     // 필요한 작업은 여기서 모두 수행합니다.


    }


    }


using 구문의 시작에서 개체를 생성합니다. 그리고 필요한 작업을 수행한 뒤 블럭이 끝나는 지점에서 Finally 블럭에 써주었던 행위를 런타임이 대신 이행합니다. using 문을 사용할 때 주의하실 점은, using에서 할당한 개체는 using 블럭이 종결되는 영역 밖에서는 유효하지 않은 개체가 된다는 점입니다. 또한 using 블럭은 특정 메서드, 프로퍼티 안에서만 쓸 수 있는 코드이며 클래스의 멤버 개체에 대해 이 구문을 사용하는 것은 특별한 유형이 아닌 이상 바람직하지 않습니다.

댓글 남기기