2010-08-05 5 views
45

외부 변수 트랩이란 정확히 무엇입니까? C#의 설명과 예제가 좋습니다.외부 변수 트랩

편집 : 존 소총의 절대적 명령을 : 통합

Eric Lippert on the Outer Variable Trap

+3

내가봤을 때까지 내가 무슨 말을했는지 전혀 알지 못했다. 그렇게하면서 많은 설명과 예제 (C#)를 발견 했으므로 다른 무엇을 찾고 있습니까? – Marc

+2

@ 마르크 어쩌면 OP는 가능한 모든 관련 프로그래밍 질문에 대한 답을 얻기를 원하는 사람 중 한 명입니다 (분명히 더 많은 것은 분명히 있습니다). 이 중 하나에 대한 대답은 분명히 누락되었습니다. –

+1

@Maciej, 우수. 마스터리스트 완성에 한 걸음 더 가깝습니다! 웹 우성, 여기 우리가 간다! – Marc

답변

61

개발자가 변수의 값이 실제로 람다 식 또는 익명의 위임에 의해 캡처 할 것으로 예상 할 때 "외부 변수 함정"발생 변수 자체가 캡처됩니다.

예 :

var actions = new List<Action>(); 
for (var i = 0; i < 10; i++) 
{ 
    actions.Add(() => Console.Write("{0} ", i)); 
} 
foreach (var action in actions) 
{ 
    action(); 
} 

출력 가능 # 1 :

0 1 2 3 4 5 6 7 8 9 

출력 가능 # 2 :

10 10 10 10 10 10 10 10 10 10 
경우

당신은 Outer Variable Trap에 빠졌습니다. 출력 # 2를 얻습니다.

수정 :

는 "내부 변수"를 선언 반복적으로 대신 한 번만 포착되는 "외부 변수"의 캡처합니다.

var actions = new List<Action>(); 
for (var i = 0; i < 10; i++) 
{ 
    var j = i; 
    actions.Add(() => Console.Write("{0} ", j)); 
} 
foreach (var action in actions) 
{ 
    action(); 
} 

자세한 내용은 Eric Lippert's blog도 참조하십시오.

foreach (var s in strings) 
    var x = results.Where(r => (r.Text).Contains(s)); 

같은

+1

"trap"이라는 단어에 흥미로운 말투가 있습니다. 변수가 캡처됩니다. (함정에 빠졌습니다) 문제가 생겼을 때 (함정에 빠졌습니다) –

+4

에릭 리 퍼트의 블로그 게시물에 링크를 추가 할 수있는 기회가 주어 졌을 때 감안할 때? http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx –

+0

그래서 j는 기본적으로 '신선한'변수이므로 k 번째 동작은 루프 변수에 의해 가정 된 k 번째 값에 바인딩 된 변수 j_k를 포함합니다. 그 결과 예상되는 동작이 얻어집니다. –

4

뭔가는 각 반복에 대해 실행되지 않습니다 포함하고 있기 때문에 당신이 기대하고있는 결과를 제공하지 않음. 루프 내부의 임시 변수에 s를 할당하면 문제를 해결할 수 있습니다.

+0

'var ='문법에 익숙하지 않다. 무엇을 하는가? = P – Marc

+0

Nitpick :'Contains'는 매 반복마다 실행되지만,'s'는 놀랍게도 항상 같은 값을 갖습니다. – dtb

+0

@dtb @Marc 그래, dtb가 말한거야. 나는 그걸 잘 설명하지 않을거야. var은 반환 형식을 명시 적으로 선언하는 대신 사용할 수있는 대안입니다 (실제 코드에서는 여기에 반환 형식을 실제로 선언 하겠지만 IDE가 아니기 때문에 실제로 반환되는 위치를 찾는 것처럼 느껴지지 않습니다 ...) IEnumberable <>) – heisenberg

1

@dtb가 정확하지만 (큰 +1) 클로저의 범위가 루프 밖으로 확장되는 경우에만 적용된다는 점에 유의해야합니다. 예를 들어 :

var objects = new [] 
    { 
     new { Name = "Bill", Id = 1 }, 
     new { Name = "Bob", Id = 5 }, 
     new { Name = "David", Id = 9 } 
    }; 

for (var i = 0; i < 10; i++) 
{ 
    var match = objects.SingleOrDefault(x => x.Id == i); 

    if (match != null) 
    { 
     Console.WriteLine("i: {0} match: {1}", i, match.Name); 
    } 
} 

이 인쇄됩니다 :

i: 1 match: Bill 
i: 5 match: Bob 
i: 9 match: David

ReSharper 안전하게이 경우 무시 될 수있다 "수정 폐쇄에 대한 액세스"에 대해 경고합니다.

0

폐쇄의 개념을 설명하는이 문서가 도움이된다 : 또한

http://en.wikipedia.org/wiki/Closure_(computer_science)는,이 문서가보다 구체적인 C#을 구현에서 정말 좋은 어쨌든

http://blogs.msdn.com/b/abhinaba/archive/2005/08/08/448939.aspx

의 TL ; lr 변수 범위는 익명의 위임 또는 람다 식에서와 마찬가지로 코드 내의 다른 모든 부분과 마찬가지로 중요합니다.이 동작은 명백하지 않습니다.

0

그것은이 트랩이 지금 가까운 루프 변수의 새 복사본 각 시간에 foreach 루프 폐쇄 내부 즉, C# 5.0부터 너무하지만 has been changedforeach 루프 존재하는 것을주의하는 가치가있다. 그래서 아래 코드 :

var values = new List<int>() { 100, 110, 120 }; 
var funcs = new List<Func<int>>(); 
foreach (var v in values) 
    funcs.Add(() => v); 
foreach (var f in funcs) 
    Console.WriteLine(f()); 

인쇄 120 120 120< C# 5.0하지만

100 110 120> = C# 5.0 그러나 for 루프는 여전히 같은 방식으로 작동합니다.