C#에서 using 키워드라고 하면 '네임스페이스를 추가하는 키워드'라는 것이 먼저 떠오를 것이다.
하지만 using 키워드는 네임스페이스 추가 기능 이외 또 다른 기능이 있다.
XNA 어플리케이션에서는 using이 '다른 기능'이 활용되는 것을 직접 볼 수 있다.
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
using (Game game = new Game())
{
game.Run();
}
}
}
using이 하나의 블록을 이룬다. 그리고 변수를 선언하는 것을 볼 수 있다.
결론부터 말하자면, using의 또 다른 기능은 IDisposable을 구현한 객체를 읽기 전용으로 사용하는 것을 자동으로 관리해주는 기능이다. using 블록에서 선언한 변수는 using 블록 내부에서만 접근할 수 있으며 블록이 종료되면 Dispose()를 호출시키고 변수를 소멸한다. using 외부에서 선언한 변수도 using 블록에 할당할 수 있다. 하지만 using 블록이 종료되면 Dispose()가 호출되어 null이 될 수 있으나, 스코프가 유지되므로 다른 변수가 참조하여 문제를 일으킬 수 있다.
Disposing dis = new Disposing();
using (dis)
{
// some do
} // dis.Dispose()
int val = dis.Value; // ObjectDisposedException
어떤 경우에 활용할 수 있을까?
반드시 Dispose()를 호출해서 소멸해야 하는 객체에서 몇 가지 값을 읽은 다음 자동으로 폐기하고 싶을 때 적절하다.
아래 시나리오를 보자.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace UsingExample { class Program { static void Main(string[] args) { Console.WriteLine("Before Manual Disposed Dummies Count : " + Dummy.Dummies.Count); CreateDummyByManual(); Console.WriteLine("After Manual Disposed Dummies Count : " + Dummy.Dummies.Count); Console.WriteLine("Before Mistake Dummies Count : " + Dummy.Dummies.Count); CreateDummyByMistake(); Console.WriteLine("After Mistake Dummies Count : " + Dummy.Dummies.Count); Console.WriteLine("Before Using Dummies Count : " + Dummy.Dummies.Count); CreateDummyByUsing(); Console.WriteLine("After Using Dummies Count : " + Dummy.Dummies.Count); Console.ReadLine(); } static void CreateDummyByManual() { Dummy dummy = new Dummy(); Console.WriteLine("CreateDummyByManual() Dummies Count : " + Dummy.Dummies.Count); dummy.Dispose(); return; } static void CreateDummyByMistake() { Dummy dummy = new Dummy(); Console.WriteLine("CreateDummyByMistake() Dummies Count : " + Dummy.Dummies.Count); return; } static void CreateDummyByUsing() { using (Dummy dummy = new Dummy()) { Console.WriteLine("CreateDummyByUsing() Dummies Count : " + Dummy.Dummies.Count); } return; } } public class Dummy : IDisposable { public static ListDummies = new List (); public Dummy() { Dummies.Add(this); } public void Dispose() { Dummies.Remove(this); Console.WriteLine("Dispose Called."); } } }
결과.
Before Manual Disposed Dummies Count : 0
CreateDummyByManual() Dummies Count : 1
Dispose Called.
After Manual Disposed Dummies Count : 0
Before Mistake Dummies Count : 0
CreateDummyByMistake() Dummies Count : 1
After Mistake Dummies Count : 1
Before Using Dummies Count : 1
CreateDummyByUsing() Dummies Count : 2
Dispose Called.
After Using Dummies Count : 1
특정 객체가 생성됨과 동시에 특정 배열에 참조를 보낸다.
Dispose()를 호출하지 않는 이상 그 객체의 참조는 배열에 유지된다.
그런데 프로그래머가 실수로 이 개체의 새 객체를 생성한 뒤 값을 읽고 작업을 마친 다음,
Dispose()를 호출하지 않고 함수를 종료하고 리턴했다.
함수가 리턴했지만 그 객체는 메모리에 그대로 남게 된다. 왜냐하면 함수 내부에서 선언한 참조는 소멸되었으나 배열에서의 참조가 남아 있기 때문이다.
게다가 배열에 여러 객체의 참조가 이미 들어 있다면 제거해야 할 그 객체를 찾기가 곤란해 진다.
이런 시나리오에서 프로그래머의 실수를 막아주는 것이 using구문이라 할 수 있다.
다음은 위 시나리오를 직접 코딩한 뒤 출력한 결과이다.
using을 이용한 경우는 자동으로 Dispose()가 호출되는 것을 알 수 있다.
반면 프로그래머가 실수를 한 뒤 배열의 요소 수를 보면 Dispose()가 호출되지 않아 그대로 배열에 상주하고 있다.
실수한 객체가 한 두 개라하면 적당히 인덱스를 줘서 지울 수도 있겠으나 빈번히 추가된다면 손 쓸 방법이 없게 된다.
이런 문제를 미연에 방지하고자 있는 구문이 using 구문이다.
'Development > C#' 카테고리의 다른 글
Enum 변환 (0) | 2013.02.13 |
---|---|
LINQ Sample (0) | 2013.01.31 |
제네릭(Generic)과 제약조건 (0) | 2012.12.28 |
IPAddress 가져오기 (0) | 2012.12.21 |
List 매서드 재정의 (0) | 2012.12.13 |