다음 본문은 도서 이펙티브 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()메서드가 호출될 수 있도록 코드를 작성해야 한다.
'🌍 C# Study > 이펙티브 C#' 카테고리의 다른 글
[48] 강력한 예외 보증을 준수하는 것이 좋다 (0) | 2021.05.13 |
---|---|
[47] 사용자 지정 예외 클래스를 완벽하게 작성하라 (0) | 2021.05.13 |
[45] 메서드가 실패했음을 알리기 위해서 예외를 이용하라 (0) | 2021.05.12 |
[44] 바인딩 된 변수는 수정하지 말라 (0) | 2021.05.12 |
[43] 쿼리 결과의 의미를 명확히 강제하고, Single()과 First()를 사용하라 (0) | 2021.05.12 |