[c#] 종료 경고에서 foreach 변수에 액세스

다음과 같은 경고가 표시됩니다.

클로저에서 foreach 변수에 액세스합니다. 다른 버전의 컴파일러로 컴파일하면 다른 동작이 발생할 수 있습니다.

내 편집기에서 다음과 같이 보입니다.

호버 팝업에서 위에서 언급 한 오류 메시지

이 경고를 수정하는 방법을 알고 있지만이 경고가 표시되는 이유를 알고 싶습니다.

“CLR”버전에 관한 것입니까? “IL”과 관련이 있습니까?



답변

이 경고에는 두 부분이 있습니다. 첫 번째는 …

클로저에서 foreach 변수에 대한 액세스

… 그것은 그 자체로 유효하지 않지만 언뜻보기에는 반 직관적입니다. 올바른 일을하는 것도 매우 어렵습니다. (아래에 링크 한 기사에서 “유해한”것으로 설명합니다.)

발췌 한 코드는 기본적으로 C # 컴파일러 (C # 5 이전)가 foreach1에 대해 생성하는 것의 확장 된 형식이라는 점에 유의하십시오 .

나는 [다음이] 유효하지 않은 이유를 이해하지 못합니다.

string s; while (enumerator.MoveNext()) { s = enumerator.Current; ...

글쎄, 그것은 구문 적으로 유효합니다. 그리고 루프에서 수행하는 모든 작업이의 을 사용하는 s것이라면 모든 것이 좋습니다. 그러나 마무리 s는 반 직관적 인 행동으로 이어질 것입니다. 다음 코드를 살펴보십시오.

var countingActions = new List<Action>();

var numbers = from n in Enumerable.Range(1, 5)
              select n.ToString(CultureInfo.InvariantCulture);

using (var enumerator = numbers.GetEnumerator())
{
    string s;

    while (enumerator.MoveNext())
    {
        s = enumerator.Current;

        Console.WriteLine("Creating an action where s == {0}", s);
        Action action = () => Console.WriteLine("s == {0}", s);

        countingActions.Add(action);
    }
}

이 코드를 실행하면 다음 콘솔 출력이 표시됩니다.

Creating an action where s == 1
Creating an action where s == 2
Creating an action where s == 3
Creating an action where s == 4
Creating an action where s == 5

이것이 당신이 기대하는 것입니다.

예상치 못한 내용을 보려면 위 코드 바로 뒤에 다음 코드를 실행하세요 .

foreach (var action in countingActions)
    action();

다음 콘솔 출력이 표시됩니다.

s == 5
s == 5
s == 5
s == 5
s == 5

왜? 우리는 모두 똑같은 일을하는 5 개의 함수를 만들었 기 때문에 : s(우리가 닫은) 값을 인쇄하십시오 . 실제로는 동일한 기능 ( “인쇄 s“, “인쇄 s“, “인쇄 s“…)입니다.

우리가 그것들을 사용하기 위해가는 시점에서 그들은 우리가 요구하는 것을 정확하게 수행합니다 : s. 당신의 마지막 알려진 값을 보면 s, 당신은 그것의 것을 볼 수 있습니다 5. 그래서 우리 s == 5는 콘솔에 5 번 인쇄됩니다.

정확히 우리가 요청한 것이지만 우리가 원하는 것은 아닐 것입니다.

경고의 두 번째 부분은 …

다른 버전의 컴파일러로 컴파일하면 다른 동작이 발생할 수 있습니다.

… 그것입니다. C # 5부터 컴파일러는 .NET을 통해 이러한 일이 발생하지 않도록 “방지”하는 다른 코드를 생성foreach 합니다.

따라서 다음 코드는 다른 버전의 컴파일러에서 다른 결과를 생성합니다.

foreach (var n in numbers)
{
    Action action = () => Console.WriteLine("n == {0}", n);
    countingActions.Add(action);
}

결과적으로 R # 경고도 생성됩니다. 🙂

위의 첫 번째 코드 스 니펫은 사용하지 않기 때문에 모든 버전의 컴파일러에서 동일한 동작을 나타냅니다 foreach(대신 C # 5 이전 컴파일러가 수행하는 방식으로 확장했습니다).

CLR 버전입니까?

나는 당신이 여기서 무엇을 요구하고 있는지 잘 모르겠습니다.

Eric Lippert의 게시물에 따르면 변경 사항은 “C # 5″에서 발생합니다. 그래서아마도 .NET 4.5 이상을 대상으로해야합니다. C # 5 이상의 컴파일러를 사용하여 새로운 동작을 가져오고 그 이전의 모든 것은 이전 동작을 가져옵니다.

하지만 명확하게 말하자면 .NET Framework 버전이 아니라 컴파일러의 기능입니다.

IL과 관련이 있습니까?

다른 코드는 다른 IL을 생성하므로 생성 된 IL에 대한 결과가 있습니다.

1 foreach 은 댓글에 게시 한 코드보다 훨씬 더 일반적인 구성입니다. 이 문제는 일반적으로 foreach수동 열거가 아닌를 사용하여 발생합니다 . 그렇기 때문에 foreachC # 5 의 변경 사항 이이 문제를 방지하는 데 도움이되지만 완전히는 아닙니다.


답변

첫 번째 대답은 훌륭해서 한 가지만 추가하겠다고 생각했습니다.

예제 코드에서 reflectModel에 IEnumerable이 할당 되었기 때문에 경고가 발생합니다. IEnumerable은 열거 할 때만 평가되며, reflectModel을 더 넓은 범위의 항목에 할당하면 열거 자체가 루프 외부에서 발생할 수 있습니다. .

당신이 변했다면

...Where(x => x.Name == property.Value)

…에

...Where(x => x.Name == property.Value).ToList()

그런 다음 reflectModel에 foreach 루프 내에서 명확한 목록이 할당되므로 열거가 루프 외부가 아닌 루프 내에서 확실히 발생하므로 경고를받지 못할 것입니다.


답변

블록 범위 변수는 경고를 해결해야합니다.

foreach (var entry in entries)
{
   var en = entry;
   var result = DoSomeAction(o => o.Action(en));
}


답변