What’s A Closure?
Closure는 자신의 코드 블록 상위에 선언된 코드를 포함하는 함수이다. 따라서 함수는 자신의 코드 블록이 포함되어 있는 상위 요소를 참조한다. 이 경우 C# 2.0에서는 익명 메소드(anonymous method)가 해당되고, 이 익명 메소드는 자신의 코드 블록 상위의 메소드에 포함된다. 이것은 부모 메소드에 존재하는 지역 변수를 익명 메소드의 코드 블록에 참조 할 수 있다는 것을 의미한다. 아래의 코드는 우리가 예상하는 것과 같이 콘솔 창에 0을 출력하게 된다. delegate void Action(); 대부분의 개발자들은 위 코드에 대해서 아무런 의문도 갖지 않는다. 지역변수 “x”는 선언되자마자 0으로 초기화 되었고, 그리고 나서 Action 타입의 delegate 변수 “a”가 선언 된 후, 콘솔 창에 변수 “x”를 출력하는 익명 메소드로 할당 되었다. 코드는 마지막으로 “a”를 호출하면서, 그 결과로 콘솔에 변수 “x”의 값을 출력한다. 여기에서 문제는 코드가 아래처럼 변경되었을 때 나타난다. delegate void Action(); 현재 “x”는 delegate 변수 “a”가 호출되기 이전에 값 1로 다시 할당되었다. 그렇다면 콘솔 창에 출력되는 값은 무엇일까? 정답은 0이 아니라 1이다. 이렇게 된 이유는, 익명 메소드는 closure이고 반드시 부모 메소드의 코드 블록 안에 위치해야 하며, 지역 변수를 자신의 코드 블록 안에 포함해야 한다. 중요한 차이점은 그것은 반드시 값이 아닌 변수를 참조한다는 것이다. 다른 말로 “x”의 값은 “a”가 익명 메소드로 할당 된 후에 “x”의 값이 복사되지 않고 “x”의 참조가 사용된다. 그렇기 때문에 “a”는 항상 “a”가 호출되기 전에 가장 마지막으로 할당된 “x”의 값을 사용 할 것이다. 사실 “x”의 참조는 “x”가 포함 된 코드블럭을 벗어난다 해도 지속될 것이다. 아래 코드를 살펴보자. delegate void Action(); 위 코드는 “a”가 호출되는 시점에서 변수 “x”의 실행 범위가 끝났다 하더라도, 호출된 “a”의 결과값은, 어김없이 콘솔 창에 1을 출력할 것이다. 어떻게 이렇게 되었을까? 이것은 컴파일러를 통해 처리되었다. 런타임은 closure를 처리하기 위해 아무런 지원도 하지 않는다. 이 말은 여러분은 익명 메소드의 사용 없이도 closure를 처리하기 위해 컴파일러가 처리하는 것과 같은 동일한 방법을 사용 할 수 있다는 것이다. 실제로 이 방법은 C# 1.0에서도 동작한다.
static void Main(string[] args)
{
int x = 0;
Action a = delegate { Console.WriteLine(x); };
a();
}
static void Main(string[] args)
{
int x = 0;
Action a = delegate { Console.WriteLine(x); };
x = 1;
a();
}
static Action GetAction()
{
int x = 0;
Action a = delegate { Console.WriteLine(x); };
x = 1;
return a;
}
static void Main(string[] args)
{
Action a = GetAction();
a();
}
댓글 없음:
댓글 쓰기