클로저(Closure)

Coding Cat·2024년 7월 17일

C#

목록 보기
9/11

클로저

클로저는 함수가 선언될 때의 환경을 '캡처(Capture)'하여, 그 환경 밖에서도 해당 환경에 접근할 수 있게 하는 기능이다. 내부 함수가 외부 함수의 스코프(Scope)에 접근할 수 있도록 해준다.

외부 변수나 필드와 같은 주변 환경을 저장한다는 건, 값이 아니라 변수 그 자체를 참조하여 저장한다는 의미이다.

클로저(Closure)는 메서드, 익명 메서드, 또는 람다 표현식 내에서 정의된 변수를 해당 메서드, 익명 메서드, 또는 람다 표현식 외부에서 정의된 변수와 함께 사용할 수 있게 해주는 기능을 말한다. 쉽게 말해서, 클로저는 외부 변수의 스코프를 캡처하고 저장하여 해당 변수가 더 이상 존재하지 않는 경우에도 접근할 수 있게 한다.

Closure : for문의 변수를 람다식에서 참조

using System;

class Program
{
    static void Main()
    {
        int outerVariable = 5;

        Func<int, int> closure = (x) =>
        {
            return x + outerVariable;
        };

        Console.WriteLine(closure(3)); // 출력: 8
    }
}

클로저의 특징

1.변수 캡처: 클로저는 메서드, 익명 메서드, 또는 람다 표현식 외부의 변수를 캡처할 수 있다. 이 변수는 메서드가 실행된 이후에도 클로저에 의해 사용될 수 있다.

2.수명 관리: 캡처된 변수의 수명은 클로저의 수명에 의해 결정된다. 따라서 클로저가 살아있는 한 캡처된 변수도 살아있게 된다.

  • 클로저가 캡처한 변수는 일반적으로 힙 메모리에 저장된다. 힙 메모리에 저장되기 때문에 함수 스코프가 종료되어도 해당 메모리 영역은 여전히 접근 가능하다.

  • 클로저가 외부 변수를 참조하고 있다면, 그 변수는 원래의 스코프가 종료되더라도 가비지 컬렉션에 의해 제거되지 않고 클로저가 살아 있는 한 유지된다.

3.참조 방식: 클로저는 변수를 값으로 캡처하는 것이 아니라 참조로 캡처한다. 따라서 캡처된 변수의 값이 변경되면, 클로저 내에서도 변경된 값이 반영된다.

루프 안에서 클로저 사용하기

using System;

class Program
{
    static void Main()
    {
        Action[] actions = new Action[5];

        for (int i = 0; i < 5; i++)
        {
            actions[i] = () => Console.WriteLine(i);
        }

        foreach (var action in actions)
        {
            action(); // 모두 5를 출력
        }
    }
}

위 예제에서 모든 클로저가 같은 i 변수에 대한 참조를 가지고 있기 때문에, 루프가 끝난 후 i의 최종 값인 5가 출력된다. 이를 해결하려면 루프 내부에서 새로운 변수를 선언해야 한다.

using System;

class Program
{
    static void Main()
    {
        Action[] actions = new Action[5];

        for (int i = 0; i < 5; i++)
        {
            int current = i;
            actions[current] = () => Console.WriteLine(current);
        }

        foreach (var action in actions)
        {
            action(); // 0, 1, 2, 3, 4 출력
        }
    }
}

이벤트 핸들러에서 클로저 사용

using System;
using System.Windows.Forms;

class Program
{
    static void Main()
    {
        Form form = new Form();
        Button button = new Button { Text = "Click Me" };
        form.Controls.Add(button);

        int clickCount = 0;

        button.Click += (sender, e) =>
        {
            clickCount++;
            Console.WriteLine($"Button clicked {clickCount} times.");
        };

        Application.Run(form);
    }
}

위 예제에서 clickCount 변수는 버튼 클릭 이벤트 핸들러에 의해 캡처되고, 버튼이 클릭될 때마다 증가한다. 이 변수는 핸들러 함수가 호출될 때마다 최신 상태로 유지된다.

비동기 작업에서 클로저 사용

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        int outerVariable = 10;

        Func<Task> asyncClosure = async () =>
        {
            await Task.Delay(1000);
            Console.WriteLine(outerVariable);
        };

        outerVariable = 20;

        await asyncClosure(); // 20 출력
    }
}

이 예제에서 비동기 람다 함수는 outerVariable을 캡처하고 있다. await Task.Delay(1000)으로 인해 비동기 작업이 완료되기 전에 outerVariable의 값이 20으로 변경되었기 때문에 최종 출력은 20이 된다.

함수형 프로그래밍에서 클로저 사용

using System;

class Program
{
    static void Main()
    {
        Func<int> CreateCounter()
        {
            int count = 0;
            return () =>
            {
                count++;
                return count;
            };
        }

        var counter = CreateCounter();

        Console.WriteLine(counter()); // 1
        Console.WriteLine(counter()); // 2
        Console.WriteLine(counter()); // 3
    }
}

이 예제에서 CreateCounter 함수는 count 변수를 캡처하고, 이를 이용하여 호출할 때마다 증가하는 카운터 함수를 반환한다. 이 카운터 함수는 클로저를 통해 count 변수의 상태를 유지한다.

profile
나의 archive

0개의 댓글