본문으로 바로가기

using 과 Dispose

category Development/C# 2013. 1. 4. 18:05

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 List Dummies = 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