Frage

Eine neue Funktion in C # / .NET 4.0 ist, dass Sie Ihre enumerable in einem foreach, ohne sich um die Ausnahme ändern können. Siehe Paul Jackson Blogeintrag Ein interessanter Nebeneffekt von Concurrency: Item aus einer Sammlung Während Aufzählen , um Informationen zu dieser Änderung Entfernen

.

Was ist der beste Weg, um die folgenden zu tun?

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

Normalerweise verwende ich ein IList als Cache / Puffer bis zum Ende des foreach, aber gibt es eine bessere Art und Weise?

War es hilfreich?

Lösung

Die Sammlung in foreach verwendet wird, ist unveränderlich. Das ist sehr viel von Entwurf.

Wie heißt es auf MSDN :

  

Die foreach-Anweisung wird verwendet, um   iterieren durch die Sammlung zu erhalten   die Informationen, die Sie wollen, aber können   nicht verwendet werden, um Elemente hinzuzufügen oder zu entfernen   von der Quellensammlung zu vermeiden   unvorhersehbare Nebenwirkungen. Wenn Sie   müssen hinzufügen oder entfernen Sie Elemente aus der   Quellensammlung, eine for-Schleife verwenden.

Der Eintrag in dem Link bereitgestellt von Poko darauf hinweist, dass in den neuen gleichzeitigen Sammlungen erlaubt.

Andere Tipps

Erstellen Sie eine Kopie der Aufzählung, eine IEnumerable-Extension-Methode in diesem Fall verwendet wird, und über sie aufzuzählen. Dies würde in jedem inneren enumerable zu dieser Aufzählung eine Kopie jedes Element hinzuzufügen.

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

Wie bereits erwähnt, aber mit einem Codebeispiel:

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

Um Nippysaurus Antwort veranschaulichen: Wenn Sie auf gehen hinzufügen die neuen Elemente in die Liste und wollen die neu hinzugefügten Elemente auch während der gleichen Aufzählung verarbeiten, dann können Sie einfach für Schleife anstelle von foreach Schleife, Problem gelöst:)

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

Für runnable Beispiel:

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

Ausgabe des letzten Beispiel:

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

Liste (15 Artikel)
0
1
2
3
4
5
6
7
8
9
1
3
5
7
9

Hier ist, wie Sie das tun können (quick and dirty Lösung Wenn Sie wirklich diese Art von Verhalten benötigen, sollten Sie entweder Ihr Design überdenken oder alle IList<T> Mitglieder außer Kraft setzen und die Quellenliste aggregieren.):

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 ist sehr effektiv für die mit Sammlungen jonglieren.

Ihre Typen und Struktur sind mir unklar, aber ich werde versuchen, Ihrem Beispiel zu den besten meiner Fähigkeit zu passen.

Von Ihrem Code scheint es, dass für jeden Punkt, den Sie hinzufügen zu diesem Artikel alles aus dem eigenen 'Enumerable Eigentum. Dies ist sehr einfach:

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

Als ein allgemeines Beispiel, sagen wir, wir wollen eine Sammlung iterieren und Elemente zu entfernen, wo eine bestimmte Bedingung erfüllt ist. Vermeiden foreach, mit LINQ:

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

ein Element hinzufügen auf jedem vorhandenen Artikel basiert? Kein Problem:

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

Sie können nicht die zählbare Sammlung ändern, während es aufgezählt wird, so dass Sie Ihre Änderungen vornehmen müssen vor oder nach dem Aufzählen.

Die for Schleife ist eine gute Alternative, aber wenn Ihre IEnumerable Sammlung ICollection nicht implementiert, es ist nicht möglich.

Entweder:

1) Kopie Sammlung zuerst. Aufzählen der kopierte Sammlung und ändern die ursprüngliche Sammlung während der Aufzählung. (@Tvanfosson)

oder

2) eine Liste der Änderungen halten und sie nach der Auszählung begehen.

Der beste Ansatz aus Sicht der Leistung ist wahrscheinlich eine oder zwei Arrays zu verwenden. Kopieren Sie die Liste in ein Array, tun Operationen auf dem Array, und dann eine neue Liste aus dem Array aufzubauen. ein Array-Element Zugriff ist schneller als ein Listenelement zugreifen, und Umwandlungen zwischen einem List<T> und einem T[] können eine schnelle „bulk Kopier-Operation“ verwendet werden, die den Aufwand vermeidet einzelne Elemente zugeordnet Zugriff.

Zum Beispiel: Angenommen, Sie haben einen List<string> und wünschen, dass jede Zeichenfolge in der Liste zu haben, die mit beginnt T durch einen Punkt „Boo“ gefolgt werden, während jede Zeichenfolge, die mit „U“ beginnt ganz fallen gelassen wird. Ein optimaler Ansatz wahrscheinlich so etwas wie wäre:

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
}

Es wäre hilfreich gewesen, wenn List<T> ein Verfahren bereitgestellt, um eine Liste von einem Teil eines Arrays zu konstruieren, aber ich bin keine Kenntnis von einem effizienten Verfahren dafür. Dennoch sind die Operationen auf Arrays ziemlich schnell. Bemerkenswert ist die Tatsache, dass das Hinzufügen und Elemente aus der Liste zu entfernen nicht erforderlich ist, um andere Elemente „Schieben“; wird jedes Element direkt an seine entsprechende Stelle in dem Array geschrieben.

Sie sollten wirklich verwenden for() statt foreach() in diesem Fall.

Timo Antwort LINQ hinzufügen kann auch wie folgt verwendet werden:

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

Ich habe einen einfachen Schritt geschrieben, aber wegen dieser Leistung wird verschlechtert

Hier ist mein Code-Schnipsel: -

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;
                                }
                            }
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top