문제

C#/.NET 4.0의 새로운 기능은 열거형을 변경할 수 있다는 것입니다. foreach 예외를 얻지 않고.Paul Jackson의 블로그 항목 보기 동시성의 흥미로운 부작용:열거하는 동안 컬렉션에서 항목 제거 이 변경 사항에 대한 자세한 내용은

다음을 수행하는 가장 좋은 방법은 무엇입니까?

foreach(var item in Enumerable)
{
    foreach(var item2 in item.Enumerable)
    {
        item.Add(new item2)
    }
}

보통 나는 IList 끝까지 캐시/버퍼로 foreach, 그런데 더 좋은 방법이 있나요?

도움이 되었습니까?

해결책

Foreach에 사용 된 컬렉션은 불변입니다. 이것은 디자인에 의한 것입니다.

말한 것처럼 MSDN:

Foreach 문은 원하는 정보를 얻기 위해 컬렉션을 반복하는 데 사용되지만 예측할 수없는 부작용을 피하기 위해 소스 컬렉션에서 항목을 추가하거나 제거하는 데 사용할 수 없습니다. 소스 컬렉션에서 항목을 추가하거나 제거 해야하는 경우 For Loop을 사용하십시오.

그 게시물 링크 Poko가 제공하는 것은 이것이 새로운 동시 컬렉션에서 허용됨을 나타냅니다.

다른 팁

이 경우 ienumerable 확장 방법을 사용하여 열거 사본을 만들고이를 열거하십시오. 이것은 그 열거에 열거 할 수있는 모든 내부의 모든 요소의 사본을 추가합니다.

foreach(var item in Enumerable)
{
    foreach(var item2 in item.Enumerable.ToList())
    {
        item.Add(item2)
    }
}

언급했듯이 코드 샘플과 함께 :

foreach(var item in collection.ToArray())
    collection.Add(new Item...);

Nippysaurus의 답변을 설명하려면 : 추가하다 목록에 새 항목을 사용하고 동일한 열거 중에 새로 추가 된 항목도 처리하려고합니다. ~을 위한 대신 루프 각각 루프, 문제 해결 :)

var list = new List<YourData>();
... populate the list ...

//foreach (var entryToProcess in list)
for (int i = 0; i < list.Count; i++)
{
    var entryToProcess = list[i];

    var resultOfProcessing = DoStuffToEntry(entryToProcess);

    if (... condition ...)
        list.Add(new YourData(...));
}

실행 가능한 예 :

void Main()
{
    var list = new List<int>();
    for (int i = 0; i < 10; i++)
        list.Add(i);

    //foreach (var entry in list)
    for (int i = 0; i < list.Count; i++)
    {
        var entry = list[i];
        if (entry % 2 == 0)
            list.Add(entry + 1);

        Console.Write(entry + ", ");
    }

    Console.Write(list);
}

마지막 예제의 출력 :

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 3, 5, 7, 9,

목록 (15 개 항목)
0
1
2
3
4
5
6
7
8
9
1
3
5
7
9

다음은 그렇게 할 수있는 방법입니다 (빠르고 더러운 솔루션. 진짜 이런 종류의 행동이 필요합니다. 디자인을 재고하거나 모든 것을 재정의해야합니다. IList<T> 회원 및 소스 목록을 집계) :

using System;
using System.Collections.Generic;

namespace ConsoleApplication3
{
    public class ModifiableList<T> : List<T>
    {
        private readonly IList<T> pendingAdditions = new List<T>();
        private int activeEnumerators = 0;

        public ModifiableList(IEnumerable<T> collection) : base(collection)
        {
        }

        public ModifiableList()
        {
        }

        public new void Add(T t)
        {
            if(activeEnumerators == 0)
                base.Add(t);
            else
                pendingAdditions.Add(t);
        }

        public new IEnumerator<T> GetEnumerator()
        {
            ++activeEnumerators;

            foreach(T t in ((IList<T>)this))
                yield return t;

            --activeEnumerators;

            AddRange(pendingAdditions);
            pendingAdditions.Clear();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            ModifiableList<int> ints = new ModifiableList<int>(new int[] { 2, 4, 6, 8 });

            foreach(int i in ints)
                ints.Add(i * 2);

            foreach(int i in ints)
                Console.WriteLine(i * 2);
        }
    }
}

LINQ 컬렉션으로 저글링하는 데 매우 효과적입니다.

당신의 유형과 구조는 나에게 불분명하지만, 나는 당신의 모범을 나의 능력을 최대한 활용하려고 노력할 것입니다.

코드에서 각 항목에 대해 자체 '열거 가능한'속성에서 해당 항목에 추가하는 것으로 보입니다. 이것은 매우 간단합니다.

foreach (var item in Enumerable)
{
    item = item.AddRange(item.Enumerable));
}

보다 일반적인 예로서, 우리는 컬렉션을 반복하고 특정 조건이 사실 인 항목을 제거하고 싶다고 가정 해 봅시다. 피하기 foreach, LINQ 사용 :

myCollection = myCollection.Where(item => item.ShouldBeKept);

기존 항목을 기반으로 항목을 추가 하시겠습니까? 괜찮아요:

myCollection = myCollection.Concat(myCollection.Select(item => new Item(item.SomeProp)));

열거 가능한 컬렉션을 열거하는 동안 열거 가능한 컬렉션을 변경할 수 없으므로 열거 전후에 변경해야합니다.

그만큼 for 루프는 좋은 대안이지만 당신의 경우 IEnumerable 컬렉션이 구현되지 않습니다 ICollection, 불가능합니다.

어느 하나:

1) 먼저 수집을 복사하십시오. 복사 된 컬렉션을 열거하고 열거하는 동안 원래 컬렉션을 변경하십시오. (@tvanfosson)

또는

2) 열거 후 변경 목록을 유지하고 열거 한 후에 저지하십시오.

성능 관점에서 볼 때 가장 좋은 접근 방식은 하나 또는 두 개의 어레이를 사용하는 것입니다.목록을 배열에 복사하고 배열에 대한 작업을 수행한 다음 배열에서 새 목록을 만듭니다.배열 요소에 액세스하는 것이 목록 항목에 액세스하는 것보다 빠르며 요소 간 변환이 가능합니다. List<T> 그리고 T[] 개별 항목 액세스와 관련된 오버헤드를 방지하는 빠른 "대량 복사" 작업을 사용할 수 있습니다.

예를 들어 List<string> 목록에 다음으로 시작하는 모든 문자열을 갖고 싶습니다. T 뒤에는 "Boo" 항목이 오고 "U"로 시작하는 모든 문자열은 완전히 삭제됩니다.최적의 접근 방식은 아마도 다음과 같습니다.

int srcPtr,destPtr;
string[] arr;

srcPtr = theList.Count;
arr = new string[srcPtr*2];
theList.CopyTo(arr, theList.Count); // Copy into second half of the array
destPtr = 0;
for (; srcPtr < arr.Length; srcPtr++)
{
  string st = arr[srcPtr];
  char ch = (st ?? "!")[0]; // Get first character of string, or "!" if empty
  if (ch != 'U')
    arr[destPtr++] = st;
  if (ch == 'T')
    arr[destPtr++] = "Boo";
}
if (destPtr > arr.Length/2) // More than half of dest. array is used
{
  theList = new List<String>(arr); // Adds extra elements
  if (destPtr != arr.Length)
    theList.RemoveRange(destPtr, arr.Length-destPtr); // Chop to proper length
}
else
{
  Array.Resize(ref arr, destPtr);
  theList = new List<String>(arr); // Adds extra elements
}

그랬다면 도움이 됐을 텐데. List<T> 배열의 일부에서 목록을 구성하는 방법을 제공했지만 그렇게 하는 효율적인 방법을 알지 못합니다.그럼에도 불구하고 배열 작업은 꽤 빠릅니다.주목할만한 점은 목록에서 항목을 추가하고 제거할 때 다른 항목 주위를 "밀어넣을" 필요가 없다는 사실입니다.각 항목은 배열의 적절한 위치에 직접 기록됩니다.

당신은 정말로 사용해야합니다 for() 대신에 foreach() 이 경우.

Timo의 답변에 추가하려면 LINQ도 다음과 같이 사용할 수 있습니다.

items = items.Select(i => {

     ...
     //perform some logic adding / updating.

     return i / return new Item();
     ...

     //To remove an item simply have logic to return null.

     //Then attach the Where to filter out nulls

     return null;
     ...


}).Where(i => i != null);

나는 하나의 쉬운 단계를 썼지만이 공연으로 인해 저하 될 것입니다.

여기 내 코드 스 니펫이 있습니다 :-

for (int tempReg = 0; tempReg < reg.Matches(lines).Count; tempReg++)
                            {
                                foreach (Match match in reg.Matches(lines))
                                {
                                    var aStringBuilder = new StringBuilder(lines);
                                    aStringBuilder.Insert(startIndex, match.ToString().Replace(",", " ");
                                    lines[k] = aStringBuilder.ToString();
                                    tempReg = 0;
                                    break;
                                }
                            }
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top