「foreachの」ループ内のリストを修正するための最良の方法は何ですか?

StackOverflow https://stackoverflow.com/questions/759966

質問

のC#/。NET 4.0の新機能を使用すると、例外を得ることなくforeachであなたの列挙を変更することができるということです。ポール・ジャクソンのブログエントリ興味深い副作用を参照してください。同時実行の:。を列挙中にこの変更については、コレクションから項目を削除する

次のことを行うための最善の方法は何ですか?

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

通常、私はIListの終了時までキャッシュ/バッファとしてforeachを使用していますが、より良い方法があるのですか?

役に立ちましたか?

解決

foreachの中で使用されるコレクションは不変です。これは、設計することにより、非常に多くのです。

これは MSDN の上で言うように:

  

foreach文をするために使用されます   取得するコレクションを反復処理   必要な情報が、缶   アイテムを追加または削除するために使用されません   回避するためにソースコレクションから   予測できない副作用。あなたの場合の   から項目を追加または削除する必要があります   ソースコレクションは、forループを使用します。

リンクするでポストを提供しますポコすることにより、この新しい同時コレクションで許可されていることを示しています。

他のヒント

この場合には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の答えを説明するために、代わりに/ strong>のループの foreachののループ、問題解決:)

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));
}

より一般的な例として、我々は、コレクションを反復処理し、特定の条件が真である場合の項目を削除したいとしましょう。 LINQを使用して、foreachの回避ます:

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

既存の各項目に基づいて項目を追加しますか?問題ありません:

myCollection = myCollection.Concat(myCollection.Select(item => new Item(item.SomeProp)));
それが列挙されている間、あなたが前か列挙した後、変更を加える必要がありますので、

あなたは、列挙コレクションを変更することはできません。

forループは素晴らしい選択肢ですが、あなたのIEnumerableコレクションがICollectionを実装していない場合、それは不可能です。

次のいずれか:

最初の1)コピーコレクション。コピーされたコレクションを列挙し、列挙中に、元のコレクションを変更。 (@tvanfosson)

または

2)変更のリストを保持して列挙した後、それらをコミットします。

パフォーマンスの観点から最善のアプローチは、1つまたは2つのアレイを使用することが考えられます。配列にリストをコピーし、アレイ上の操作を行い、その後、配列から新しいリストを作成します。配列要素にアクセスするリスト項目へのアクセスよりも高速で、かつList<T>T[]間の変換は、個々のアイテムへのアクセス関連するオーバーヘッドを回避する高速「バルク・コピー」動作を使用することができます。

たとえば、あなたがList<string>を持っており、「U」で始まるすべての文字列が完全に落ちている間に、アイテム「ブー」が続くことTで始まるリスト内のすべての文字列を持っているしたいとします。最適なアプローチは、おそらくのようなものになるでしょう。

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()を使用する必要があります。

ティモの回答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);

私は1回の簡単な操作を書かれているが、このため、パフォーマンスの低下します。

ここに私のコードは、次のとおりです。 -

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