다음 본문은 도서 이펙티브 C# (빌 와그너)에서 나오는 주제를 다룹니다.
개발자는 일반적으로 블록을 벗어나면 지역변수가 가비지 콜렉터에 의해 정리될 것이라 생각하여
지역변수의 수명을 거의 신경쓰지 않는다.
하지만 클로저(Closure)는 이러한 규칙을 벗어난다.
캡처된 변수를 사용하는 마지막 델리게이트가 가비지화될 때 까지 해당 변수는 가비지로 간주되지 않는다.
일반적으로 단순 메모리 리소스만 사용한다면 적절한 시점에 가비지로 수집될 것이기 때문에 신경쓸 필요없지만
매우 무거운 리소스를 참고하고 있을경우 더욱 신경써야 한다.
var counter = 0;
var numbers = Extensions.Generate(30, () => counter++);
이 코드는 실제로 다음과 같은 코드를 생성한다.
private class Closure
{
public int generatedCounter;
public int generatorFun() => generatedCounter++;
}
// 사용 예
var c = new Closure();
c.generatedCounter = 0;
var sequence = Extensions.Generate(30, new Func<int>(c.generatorFunc));
내부적으로 중첩클래스가 정의되며, 이 클래스는 Extension.Generate가 사용하는 델리게이트에 바인딩된다.
다음과 같이 함수로 시퀀스를 반환하는 코드가 있다면,
public IEnumerable<int> MakeSequence()
{
var counter = 0;
var numbers = Extensions.Generate(30, () => counter++);
return numbers;
}
생성되는 코드는 다음과같다. Closure 객체의 레퍼런스인 c는 지역변수임에도 델리게이트에 바인딩 되었기 때문에 메서드를 벗어나서도 여전히 살아남게된다.
public static IEnumerable<int> MakeSequence()
{
var c = new Closure();
c.generatedCounter = 0;
var sequence = Extensions.Generate(30, new Func<int>(c.generatorFunc));
return sequence;
}
결론
어떤 경우에서든 클로저에 의해 생성된 객체를 메서드가 반환하는 경우 클로저를 수행하기 위해 캡처됐던 모든 변수들이 그 안에 포함된다는 사실을 알아야한다.
'🌍 C# Study > 이펙티브 C#' 카테고리의 다른 글
[43] 쿼리 결과의 의미를 명확히 강제하고, Single()과 First()를 사용하라 (0) | 2021.05.12 |
---|---|
[42] IEnumerable<T> 데이터 소스와 IQueryable<T> 데이터 소스를 구분하라 (0) | 2021.05.12 |
[40] 지연 수행과 즉시 수행을 구분하라 (0) | 2021.05.02 |
[39] function과 action 내에서는 예외가 발생하지 않도록 하라 (0) | 2021.05.02 |
[38] 메서드보다 람다 표현식이 낫다 (0) | 2021.05.02 |