[c#] List <T> .ForEach가 목록 수정을 허용하는 이유는 무엇입니까?

내가 사용하는 경우 :

var strings = new List<string> { "sample" };
foreach (string s in strings)
{
  Console.WriteLine(s);
  strings.Add(s + "!");
}

Add의는 foreach우리가 우리의 발 아래에서 양탄자를 당겨 때문에 나는, 논리적 생각하는 (열거 작업이 실행되지 않을 수 있습니다 컬렉션이 수정 된) InvalidOperationException이 발생합니다.

그러나 다음을 사용하는 경우 :

var strings = new List<string> { "sample" };
strings.ForEach(s =>
  {
    Console.WriteLine(s);
    strings.Add(s + "!");
  });

OutOfMemoryException이 발생할 때까지 루프를 실행하여 즉시 발을 쏜다.

난 항상 List.ForEach가 중 단지 래퍼라고 생각 이것은, 나에게 깜짝 온다 foreach또는 위해 for.
누구든지이 행동의 방법과 이유에 대한 설명이 있습니까?

( 끝없이 반복되는 Generic List에 대한 ForEach 루프에서 영감을 얻음 )



답변

때문입니다 ForEach열거를 사용하지 않는 방법은, 그것은을 가진 항목을 통해 루프 for루프 :

public void ForEach(Action<T> action)
{
    if (action == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    for (int i = 0; i < this._size; i++)
    {
        action(this._items[i]);
    }
}

(JustDecompile로 얻은 코드)

열거자를 사용하지 않기 때문에 목록이 변경되었는지 확인 for하지 않으며 _size반복 할 때마다 증가 하므로 루프 의 끝 조건에 도달하지 않습니다 .


답변

List<T>.ForEachfor내부를 통해 구현 되므로 열거자를 사용하지 않고 컬렉션을 수정할 수 있습니다.


답변

List 클래스에 연결된 ForEach는 내부적으로 내부 멤버에 직접 연결된 for 루프를 사용하기 때문에 .NET 프레임 워크의 소스 코드를 다운로드하여 확인할 수 있습니다.

http://referencesource.microsoft.com/netframework.aspx

foreach 루프가 무엇보다도 컴파일러 최적화이지만 관찰자로서 컬렉션에 대해 작동해야하는 경우-컬렉션이 수정되면 예외가 발생합니다.


답변

우리는이 문제에 대해 알고 있으며 원래 작성되었을 때 감독이었습니다. 안타깝게도 이전에 작동하던 코드가 실행되지 않도록 할 수 있으므로 변경할 수 없습니다.

        var list = new List<string>();
        list.Add("Foo");
        list.Add("Bar");

        list.ForEach((item) =>
        {
            if(item=="Foo")
                list.Remove(item);
        });

Eric Lippert가 지적 했듯이이 방법 자체의 유용성은 의심 스럽기 때문에 Metro 스타일 앱 (예 : Windows 8 앱) 용 .NET에는 포함하지 않았습니다.

David Kean (BCL 팀)


답변