Development Tip

클로저의 변수 캡처에 대한 자세한 설명

yourdevel 2020. 12. 25. 10:34
반응형

클로저의 변수 캡처에 대한 자세한 설명


변수 캡처가 클로저 생성을 위해 변수를 가져 오는 방법에 대한 수많은 게시물을 보았지만, 모두 특정 세부 사항이 부족하고 모든 것을 "컴파일러 마법"이라고 부르는 것 같습니다.

다음에 대한 명확한 설명을 찾고 있습니다.

  1. 지역 변수가 실제로 캡처되는 방법.
  2. 값 유형 캡처와 참조 유형 간의 차이 (있는 경우)
  3. 그리고 값 유형과 관련하여 권투가 발생하는지 여부.

내가 선호하는 것은 가치와 포인터 (내부적으로 일어나는 일의 핵심에 더 가깝다) 측면에서 대답하는 것이지만, 가치와 참조를 포함하는 명확한 대답도 받아 들일 것입니다.


  1. 까다 롭습니다. 잠시 후에 그것에 올 것입니다.
  2. 차이는 없습니다. 두 경우 모두 캡처되는 변수 자체입니다.
  3. 아니, 권투가 발생하지 않습니다.

예제를 통해 캡처가 어떻게 작동하는지 보여주는 것이 가장 쉬울 것입니다.

다음은 단일 변수를 캡처하는 람다 식을 사용하는 코드입니다.

using System;

class Test
{
    static void Main()
    {
        Action action = CreateShowAndIncrementAction();
        action();
        action();
    }

    static Action CreateShowAndIncrementAction()
    {
        Random rng = new Random();
        int counter = rng.Next(10);
        Console.WriteLine("Initial value for counter: {0}", counter);
        return () =>
        {
            Console.WriteLine(counter);
            counter++;
        };
    }
}

이제 컴파일러가 수행하는 작업이 있습니다. 단, C #에서는 실제로 발생할 수없는 "말할 수없는"이름을 사용합니다.

using System;

class Test
{
    static void Main()
    {
        Action action = CreateShowAndIncrementAction();
        action();
        action();
    }

    static Action CreateShowAndIncrementAction()
    {
        ActionHelper helper = new ActionHelper();        
        Random rng = new Random();
        helper.counter = rng.Next(10);
        Console.WriteLine("Initial value for counter: {0}", helper.counter);

        // Converts method group to a delegate, whose target will be a
        // reference to the instance of ActionHelper
        return helper.DoAction;
    }

    class ActionHelper
    {
        // Just for simplicity, make it public. I don't know if the
        // C# compiler really does.
        public int counter;

        public void DoAction()
        {
            Console.WriteLine(counter);
            counter++;
        }
    }
}

루프에서 선언 된 변수를 캡처하면 루프의 ActionHelper각 반복마다 의 새 인스턴스가 생성 되므로 변수의 서로 다른 "인스턴스"를 효과적으로 캡처 할 수 있습니다.

다른 범위에서 변수를 캡처 할 때 더 복잡해집니다. 그런 수준의 세부 정보를 정말로 원하는지 알려주세요. 아니면 코드를 작성하고 Reflector에서 디 컴파일 한 다음 따라 해보세요. :)

방법에 유의하십시오.

  • 관련된 권투가 없습니다
  • There are no pointers involved, or any other unsafe code

EDIT: Here's an example of two delegates sharing a variable. One delegate shows the current value of counter, the other increments it:

using System;

class Program
{
    static void Main(string[] args)
    {
        var tuple = CreateShowAndIncrementActions();
        var show = tuple.Item1;
        var increment = tuple.Item2;

        show(); // Prints 0
        show(); // Still prints 0
        increment();
        show(); // Now prints 1
    }

    static Tuple<Action, Action> CreateShowAndIncrementActions()
    {
        int counter = 0;
        Action show = () => { Console.WriteLine(counter); };
        Action increment = () => { counter++; };
        return Tuple.Create(show, increment);
    }
}

... and the expansion:

using System;

class Program
{
    static void Main(string[] args)
    {
        var tuple = CreateShowAndIncrementActions();
        var show = tuple.Item1;
        var increment = tuple.Item2;

        show(); // Prints 0
        show(); // Still prints 0
        increment();
        show(); // Now prints 1
    }

    static Tuple<Action, Action> CreateShowAndIncrementActions()
    {
        ActionHelper helper = new ActionHelper();
        helper.counter = 0;
        Action show = helper.Show;
        Action increment = helper.Increment;
        return Tuple.Create(show, increment);
    }

    class ActionHelper
    {
        public int counter;

        public void Show()
        {
            Console.WriteLine(counter);
        }

        public void Increment()
        {
            counter++;
        }
    }
}

ReferenceURL : https://stackoverflow.com/questions/5438307/detailed-explanation-of-variable-capture-in-closures

반응형