CodeGym /행동 /C# SELF /로컬 함수의 변수 캡처

로컬 함수의 변수 캡처

C# SELF
레벨 11 , 레슨 5
사용 가능

1. 들어가기

스코프 다시 생각해보기

변수들을 큰 사무실의 직원들이라고 생각해보자. 그리고 메서드, 반복문, 코드 블록들은 방이나 사무실 같은 거야. 어떤 직원은 자기 방에만 들어갈 수 있고, 어떤 직원은 건물 전체를 돌아다닐 수 있어. 누가 어디에 있을 수 있는지 — 이게 바로 스코프(scope)야.

스코프는 프로그램에서 선언된 변수가 어디서 “보이는지”랑 어디서 쓸 수 있는지를 정해줘.

주요 스코프 종류

C#에서는 이런 주요 스코프가 있어:

스코프 예시 변수가 "보이는" 곳
로컬 메서드나 블록 안 딱 그 블록 안에서만
메서드 파라미터 메서드 시그니처에 메서드 안에서만
클래스 변수(필드) 메서드 밖 클래스 본문에 그 클래스의 모든 메서드에서
반복문/조건문 안 변수
{}
반복문/if 안
{}
안에서만

설명 예시

public class Office
{
    
  
int buildingNumber = 50; // 클래스 필드: 모든 메서드에서 보임 public void PrintInfo() {
int roomNumber = 101; // 로컬 변수: PrintInfo 안에서만 보임 if (roomNumber > 100) {
int deskNumber = 5; // 이 if 블록 안에서만 보임 Console.WriteLine(deskNumber);
} Console.WriteLine(deskNumber); // 오류! deskNumber는 여기서 안 보임
}
}

2. 로컬 함수와 스코프

누가 누구를 "볼 수 있을까"?

메서드 안(혹은 반복문이나 조건문 안)에 로컬 함수를 선언하면, 그 함수는 위에서 선언된 변수랑 같은 스코프에 있게 돼. 로컬 함수는 마치 “같은 방의 일부”처럼 행동해.

예시

로컬 함수는 주변 스코프의 변수를 볼 수 있어


    void PrintWithPrefix(string message)
{
    
  
string prefix = "[로그]: "; void Print() {
Console.WriteLine( prefix + message); // 둘 다 볼 수 있음!
} Print();
}

여기서 prefixmessage 변수는 로컬 함수 Print 안에서 보이는데, 그 이유는 같은(혹은 더 넓은) 스코프에서 선언됐기 때문이야.

여기 스코프가 몇 개?

위 예시에서:

  • PrintWithPrefix 메서드의 스코프가 있고
  • 그 안에 Print 함수의 스코프가 있어
로컬 함수는 완전히 자기만의 “방”을 만들진 않아: 복도(메서드)에 있는 걸 다 볼 수 있어.

3. 변수 캡처(Capture)

변수 캡처란, 로컬 함수가 자기 함수 밖에서 선언됐지만 같은 스코프에 있는 변수를 사용하는 상황이야.

로컬 함수랑 익명 메서드(람다식, 이건 나중에 다룰 거야)는 선언될 때 접근할 수 있었던 모든 변수를 기억(혹은 “캡처”)해.

함수가 마치 주변 세계의 스냅샷(capture)을 찍는 것처럼 — 그리고 나중에 한참 뒤에 호출돼도 그 변수들을 쓸 수 있어.

도식적으로

Main 메서드
└─ 변수 x
└─ 로컬 함수 F() ← "x를 캡처함"

예시 — 제일 간단한 캡처

void CounterExample()
{
    int counter = 0;

    void Increase()
    {
        counter++; // 이 함수가 counter 변수를 캡처함
    }

    Increase();
    Increase();
    Console.WriteLine(counter); // 2 출력됨
}
로컬 함수가 외부 스코프의 변수를 캡처함

여기서 로컬 함수 Increase를 두 번 호출한 후 counter 값이 2로 증가해.

4. 변수 캡처의 활용

변수 캡처 덕분에 메서드 스코프랑 로컬 함수 사이에서 데이터를 편하게 “전달”할 수 있어. 쓸데없이 파라미터로 넘길 필요가 없지.

만약 캡처가 없었다면, 모든 변수를 파라미터로 넘겨야 했을 거야:

void CounterExampleWithoutCapture()
{
    int counter = 0;

    void Increase(ref int c)
    {
        c++;
    }

    Increase(ref counter);
    Increase(ref counter);
    Console.WriteLine(counter);
}

이건 귀찮지 — 계속 ref 쓰고 함수 시그니처도 지저분해지잖아. 그냥 밖에 있는 변수를 함수가 “볼 수” 있으면 훨씬 편하지!

5. 로컬 함수와 변수의 생명주기

변수가 메서드 끝나도 살아있을까?

로컬 함수(혹은 람다식이 있는 delegate)를 현재 메서드 밖으로 넘기면, 캡처된 변수들은 메서드가 끝나도 자동으로 “죽지 않아”. CLR(.NET의 가상머신)이 알아서 — 필요한 건 메모리에 “붙잡아” 둘 거야.

예시: 함수가 메서드 밖에서 살아있음

Func<int> GetCounter()
{
    int count = 0;
    int Increment()
    {
        count++;
        return count;
    }
    return Increment; // 함수를 밖으로 반환!
}

var counter = GetCounter();
Console.WriteLine(counter()); // 1
Console.WriteLine(counter()); // 2
클로저: count 변수는 메서드가 끝나도 살아있음

여기서 GetCounter 메서드가 끝난 뒤에도 count 변수는 계속 살아있어. 반환된 함수가 그걸 캡처했기 때문이지. 이걸 클로저(closure)라고 해 — 이건 따로 강의에서 더 다룰 거지만, 로컬 함수에서도 똑같이 동작해.

6. 흔한 실수와 재밌는 상황

로컬 함수 호출 전에 변수 값 바꾸기

가끔 로컬 함수가 캡처한 변수를 함수 호출 전에 바꿔버리면 — 결과가 기대랑 다를 수 있어.

예시:


void Example()
{
    int x = 42;
    void PrintX() {  Console.WriteLine(x);  } 

    x = 100; // 변수 값 바꿈!
    PrintX(); // 100 출력, 42 아님!
}

꿀팁: 로컬 함수는 항상 호출 시점의 변수 “최신 값”을 봐.

for/foreach 반복문에서 변수 캡처(한 번 더)

이건 진짜 자주 나오는 문제: 코딩 테스트나 큰 프로젝트에서 로직 짤 때, “반복문에서 살아있는 변수”를 캡처하고 있진 않은지 꼭 확인해봐. 그 변수, 네가 생각한 대로 동작 안 할 수도 있어.

1
설문조사/퀴즈
튜플이랑 로컬 함수, 레벨 11, 레슨 5
사용 불가능
튜플이랑 로컬 함수
튜플이 out이랑 익명 타입보다 좋은 점
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION