방프리

21.09.18 Chapter4. LINQ 활용 (Item 41) 본문

C#/Effective C#

21.09.18 Chapter4. LINQ 활용 (Item 41)

방프리 2021. 9. 18. 17:36

Item 41 : 값비싼 리소스를 캡처하지 말라

 

처음으로 C#에서 클로저라는 단어를 들었다.  Swift에서 사용되는 클로저와 비슷한 느낌인데

간단히 풀어서 람다 표현식이 동작하는 메서드의 지역변수를 람다 표현식에서 사용하면 클로저라고 한다.

클로저는 예시로 보는게 더 이해가 쉬울 것 같다.

public void DoSomething()
{
    int localVariable = 0;
    
    Action<int> operatorLambda = delegate(int value)
    {
    	int result = localVariable + value;
    }
}

이 클로저는 대표적으로 LINQ에서 자주 사용된다고 한다.

해당 주제는 이 클로저를 사용할 때 문제가 발생하는 것에 대해 지적한다. 

보통 개발자들은 로직을 만들 때 지역변수는 해당 블록을 벗어나면 자연스럽게 소멸되거나, 가비지 컬렉터로 

이동한다고 생각한다. 하지만 클로저는 조금 예외이다. 어느 영역에서 반환하느냐에 따라 소멸 시기가 다르기 때문이다.

소멸 시기가 다르다보니 파일 스트림을 사용 후 닫는 코드는 넣는 시점을 개발자가 알기 어렵다.

이에 따라 메모리 누수 또는 파일스트림이 닫힌 후에도 리소스에 접근하려는 코드가 생길 수 있다.

그렇다면 Dispose가 호출되는 시점에서 파일스트림을 닫으면 어떻게 될까?

public IEnumerable<int> SomeFunction()
{
    using (Generator g = new Generator())
    {
        while (true) 
        {
            yield return g.GetNextNumber();
        }
    }
}

var query = (from n in SomeFunction()
			    select n).Take(5);

foreach (var s in query)
{
    Console.WriteLine(s);
}

WriteLine("Again");

foreach (var s in query)
{
    WriteLine(s);
}

// result
// 0
// 1
// 2
// 3
// 4
// Disposing now
// Again
// 0
// 1
// 2
// 3
// 4
// Disposing now

분명 Dispose가 한 번만 출력될거라 생각하지만 그렇지 않다. 그렇기 때문에 저자는 클로저를 사용할 때

특히 무거운 리소스를 관리하는 객체라면 어느 시점에서 해제가 진행되어야 하는지? 그리고 클로저를 사용할 땐

변수를 제대로 정리하는지 확인한 상태에서 사용해야 한다고 주의를 주고 있다.

Comments