2012-07-17 2 views
0

가능한 중복 :
C#: using the iterator variable of foreach loop in a lambda expression - why fails?왜이 루프 내에서 위임이 작동하지 않습니까?

내가 MSDN에서 C#을 참조를 읽고 있었다, 나는 이걸 발견 ...

http://msdn.microsoft.com/en-us/library/0yw3tz5k.aspx

끝에 주석이있다 한평에 의해 albionmike 그것은 이렇게 .. 간다

When you "catpure" a variable from an outer scope, some counter-intuitive things happen. 
If you run this, you will get an IndexOutOfRange exception during the call f(). 
If you uncomment the two commented out lines of code, it will work as expected. 
Hint: Captured Outer Variables have reference rather than value semantics 

// Console Project 
using System; 
using System.Collections.Generic; 
using System.Text; 


namespace EvilDelegation 
{ 
    delegate void PrintIt(); 

    class Program 
    { 

     static void Main(string[] args) 
     { 
      string[] strings = { "zero", "one", "two", "three", "four" }; 
      PrintIt f = null; 
      for (int i = 0; i < strings.Length; ++i) { 
       if (i == 2 || i == 3) { 
        // Can you see why this would not work? 
        f = delegate() { Console.WriteLine(strings[i]); }; 

        // But this does... 
        //int k = i; 
        //f = delegate() { Console.WriteLine(strings[k]); }; 

       } 
      } 
      f(); 
     } 
    } 
} 

주먹이 작동하지 않는 이유는 무엇이며, 두 번째 것은 작동합니까? 제 4 라인에서 그는 다음과 같이 말합니다 : Captured Outer Variables have reference rather than value semantics.
좋습니다. 그러나 for 루프에서 iint으로 정의했는데 이는 값 유형이므로 int 유형이 참조를 보유 할 수있는 방법은 무엇입니까? 그리고 i이 참조를 보유 할 수 없다면 이는 값을 저장한다는 것을 의미하며 값을 저장하는 경우 첫 번째 값은 작동하지 않고 두 번째 값은 저장되지 않습니다.
여기에 뭔가가 있습니까?

편집 : 원본 작성자는 f() 호출이 오타가있는 것으로 생각합니다. 응답하는 동안 이것을 고려하십시오.

EDIT 2 : 좋아, 누군가가 말하면, 오타가 아니라고 생각해 봅시다. f()에 대한 전화가 if 절 안에서 이루어진 경우를 알고 싶습니다. 이 경우 둘 다 실행되거나 주석이없는 사람이 둘 중 하나입니까?

+1

이것은 문자 그대로 [Ask 18 minutes ago] (http://stackoverflow.com/questions/11524532/delegate-method-inside-foreach-loop-always-binds-to-last-item)입니다. –

+0

@KirkWoll 글쎄, 내가 그걸 생각해 내지 않았고, 검색하지 않았고, MSDN에서 찾았고 이해할 수 없었기 때문에 여기에 묻습니다. – Razort4x

답변

3

이것은 클로저의 의미 때문입니다. 클로저가 외부 범위의 로컬 변수를 참조하면 변수에 포함 된 값이 아니라 변수에 대한 참조를 캡처합니다.

이 경우 익명의 대표 delegate() { Console.WriteLine(strings[i]); }i 변수를 참조하여 을 캡처합니다. 즉 변수는 익명 함수와 i이 선언 된 범위 사이에서 공유됩니다. 하나의 컨텍스트에서 i이 변경되면 다른 컨텍스트에서도 변경됩니다.예를 들어

(see it run)

using System; 

class Foo { 
    static void Main() { 
     int i = 0; 
     Action increment = delegate { ++i; }; 

     Console.WriteLine(i); 

     ++i; 
     Console.WriteLine(i); 

     increment(); 
     Console.WriteLine(i); 

     ++i; 
     Console.WriteLine(i); 

     increment(); 
     Console.WriteLine(i); 
    } 
} 

이 출력 할 것이다 : C#으로

0 
1 
2 
3 
4 

는 로컬의 수명이를 참조하는 클로저의 유효 기간을 포함하도록 연장된다. (불행한) 디자인으로 그냥

static Func<int> Counter() { 
    int i = 0; 
    return delegate { return i++; }; 
} 
3

수정 된 클로저에 액세스하고 있습니다. 위임자는 호출 될 때만 평가되며 루프 변수 i을 캡처했습니다. 이 시간에 액세스 할 때 루프가 종료 된 후 해당 값은 strings.Length과 같습니다. 그러나 로컬 변수 k을 루프 내에 도입하면 루프의 해당 반복에 대해 특정 변수 k을 캡처하고 결과가 정확합니다.

루프 내에서 호출하는 경우 아래 설명에서 제안하는 것처럼 i 값은 루프가 진행되기 전에 그 시점에서 평가되며 "올바른"값을 갖습니다.

+0

그래, 나는 그것이 오타라고 생각하고 그것이라고해도 말합니다. 아니. 그런 다음 잠시 그것을 오타라고 간주하고'f()'에 대한 호출이'if' 절 안에 있다고 말하게합니다. 또한 같은 질문을 반영하도록 질문을 업데이트했습니다. – Razort4x

0

:이 C/C++ 개발자의 감성을 불쾌 수있는 몇 가지 꽤 흥미 트릭을 할 수 있습니다. 루프 변수 i을 대리자에 사용하면 해당 내용이 캡처되고 if() 호출에 도달하면 궁극의 가치를 갖습니다.

foreach 변수가 곧 변경 될 예정이며, this blog post에 따르면 Eric Lippert가 적용되지만, for 변수는 해당 변수가 아닙니다.

추가 : 내 .NET 버전에서

string[] strings = { "zero", "one", "two", "three", "four" }; 

    var list = new List<PrintIt>(); 

    foreach (var str in strings) 
    { 
    PrintIt f = delegate { Console.WriteLine(str); }; // captures str 
    list.Add(f); 
    } 
    var f0 = list[0]; 
    f0(); 

(.NET 4.0, C# 4) 마지막 줄에 인쇄 "네"

여기 foreach 대신 for과 예입니다. 곧 출시 될 .NET 버전 (.NET 4.5, C# 5, Visual Studio 2012)에서는 "0"으로 인쇄됩니다. 주요 변경 ... 물론

, delegate { Console.WriteLine(str); }() => { Console.WriteLine(str); } 동등 delegate() { Console.WriteLine(str); }이 경우 동일하다.

+0

이것은 불행한 디자인이 아닙니다. Closure는 매우 강력한 개념이며 매우 멋진 프로그래밍 프로그래밍 기법을 가능하게합니다. 참조에 의한 캡쳐 (capture-by-reference) 의미론은 수년간 다른 언어 (lisp, scheme)로 존재 해왔다. 오히려 얼마나 많은 사람들이 그 개념을 오해하는지 불행한 것입니다. – cdhowie

+0

@cdhowie 그래,하지만 위의 편집을 참조하십시오. Lippert가 "불행한"이야기를 설명하는 블로그 링크가 있습니다. –

+0

흠. 나는 그 행동을 예상하기 위해 폐쇄에 익숙하다고 생각한다. 그래서 나는 전혀 불행하지 않다. – cdhowie

관련 문제