본문 바로가기
프로그래밍/C#

C# 변수 캡처(Capture)에 대해서

by bantomak 2024. 1. 23.

람다 변수 캡처(Capture)

람다를 다루다 보면 변수를 캡처하는 상황을 맞이하게 될 것이다.

 

using System;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        List<Action> actions = new List<Action>();

        for (int i = 0; i < 10; ++i)
        {
            actions.Add(() => Console.WriteLine(i));
        }

        foreach (var a in actions)
        {
            a.Invoke();
        }

        // 기대하던 출력: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
        // 실제 출력:    10, 10, 10, 10, 10, 10, 10, 10, 10, 10
    }
}

 

위의 코드를 작성한 개발자의 기댓값과 실제값은 다르다. 왜냐면, C# 컴파일러는 i 값에 대한 변수를 다음과 같이 임시 생성한 클래스의 변수로 대체해 버리기 때문이다.

 

using System;
using System.Collections.Generic;

public class [임시클래스]
{
    public int _i;
    public void _f()
    {
        Console.WriteLine(_i);
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<Action> actions = new List<Action>();

        [임시클래스] _var = new [임시클래스]();

        for (_var._i = 0; _var._i < 10; ++_var._i)
        {
            actions.Add(_var._f);
        }

        foreach (Action a in actions)
        {
            a.Invoke();
        }
    }
}

 

(i를 캡처하기 위해서 만들어진 임시 클래스)

우리가 원하는 바를 이루기 위해서는 이렇게 코드를 작성해야 한다.

 

using System;
using System.Collections.Generic;
class Program
{
    static void Main(string[] args)
    {
        List<Action> actions = new List<Action>();

        for (int i = 0; i < 10; ++i)
        {
            int v = i;
            actions.Add(() => Console.WriteLine(v));
        }

        foreach (Action a in actions)
        {
            a.Invoke();
        }
    }
}

 

이렇게 되면 C# 컴파일러는 i가 아닌 v 변숫값을 캡처하기 위해 다음과 같은 식으로 for 루프 내에서 임시클래스를 생성하게 된다.

 

우리가 원하는 결과가 출력된다.

 

using System;
using System.Collections.Generic;

public class [임시클래스]
{
    public int _v;
    public void _f()
    {
        Console.WriteLine(_i);
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<Action> actions = new List<Action>();

        for (int i = 0; i < 10; ++i)
        {
            [임시클래스] _var = new [임시클래스]();
            _var._v = i;
            actions.Add(_var._f);
        }

        foreach (Action a in actions)
        {
            a.Invoke();
        }
    }
}

 

이전 포스팅에 있는 볼링 점수 계산기 코드에서도 비슷한 코드를 발견할 수 있다.

 

 

[백준 BAEKJOON] 17215번 볼링 점수 계산

문제 소현이는 친구들과 함께 볼링을 치러 볼링장에 갔다. 그런데 볼링장의 시스템 오류로 인해 점수판에 점수가 집계 되지 않는 문제가 있었다. 밖이 너무 추운 나머지 소현이와 친구들은 그냥

jettstream.tistory.com

 

var frame = m_frame 이 부분이다.

 

정리하자면 결국 변수를 캡처할 때 중요한 건 범위(Scope)라는 걸 알 수 있다.

해당 규칙을 잘 숙지해서 항상 원하는 결과가 나오도록 코딩해 보자.

 

출처

 

.NET Framework: 523. C# 람다(Lambda)에서 변수 캡처 방식

.NET Framework: 523. C# 람다(Lambda)에서 변수 캡처 방식 [링크 복사], [링크+제목 복사] 조회: 20963 글쓴 사람 정성태 (techsharer at outlook.com) 홈페이지 첨부 파일 부모글 보이기/감추기 (연관된 글이 3개 있

www.sysnet.pe.kr

댓글