Frage

Ich habe Code, den ich wollen So aussehen:

List<Type> Os;

...

foreach (Type o in Os)
    if (o.cond)
        return;  // Quitting early is important for my case!
    else
        Os.Remove(o);

... // Other code

Dies funktioniert nicht, da Sie nicht aus der Liste entfernen können, wenn Sie sich in einem befinden foreach Schleife über diese Liste:

Gibt es eine gemeinsame Möglichkeit, das Problem zu lösen?

Ich kann bei Bedarf zu einem anderen Typ wechseln.

Option 2:

List<Type> Os;

...

while (Os.Count != 0)
     if (Os[0].cond)
         return;
     else
         Os.RemoveAt(0);

... // Other code

Hässlich, aber es sollte funktionieren.

War es hilfreich?

Lösung

Müssen Sie dies wirklich innerhalb eines tun? foreach Schleife?

Dies erzielt die gleichen Ergebnisse wie Ihre Beispiele, dh alle Elemente aus der Liste bis zum ersten Element, der dem Zustand entspricht (oder alle Elemente entfernen, wenn keine davon mit der Bedingung übereinstimmt).

int index = Os.FindIndex(x => x.cond);

if (index > 0)
    Os.RemoveRange(0, index);
else if (index == -1)
    Os.Clear();

Andere Tipps

Sie können die Liste rückwärts durchlaufen:

for (int i = myList.Count - 1; i >= 0; i--)
{
    if (whatever) myList.RemoveAt(i);
}

Als Antwort auf Ihren Kommentar, wenn Sie ein Element finden, das Sie nicht entfernen, ist es die beste Lösung, wenn Sie einen Artikel finden, den Sie nicht entfernen, nur eine Weile -Schleife verwenden.

Sie sollten niemals etwas aus einer Sammlung entfernen, über die Sie sich in einem Foreach -Schleifen iterieren. Es ist im Grunde so, als würde man den Zweig sägen, auf dem Sie sitzen.

Verwenden Sie Ihre While -Alternative. Es ist der richtige Weg.

Ich bin ein Java -Programmierer, aber so etwas funktioniert:

List<Type> Os;
List<Type> Temp;
...
foreach (Type o in Os)
    if (o.cond)
        Temp.add(o);
Os.removeAll(Temp);  

Ich hatte gerade dieses Problem mit meiner Analysebibliothek. Ich habe das versucht:

for (int i = 0; i < list.Count; i++)
{                
   if (/*condition*/)
   {
       list.RemoveAt(i);
       i--;
   }
}

Es ist ziemlich einfach, aber ich habe nicht an einen Bruchpunkt gedacht.

Hier ist der Einfachste Lösung mit dem Einfachsten WARUM

PROBLEM:

Normalerweise entfernen wir aus der ursprünglichen Liste, wodurch das Problem der Aufrechterhaltung der Listenzahl und des Iterator -Standorts erzeugt wird.

List<Type> Os = ....;
Os.ForEach(
    delegate(Type o) {
        if(!o.cond) Os.Remove(o);
    }
);

LÖSUNG - LINQ.ForEach:

Beachten Sie alles, was ich hinzugefügt habe, war ToList(). Dadurch werden eine neue Liste erstellt, auf der Sie nachfragen, daher können Sie Ihre ursprüngliche Liste entfernen, aber weiterhin die gesamte Liste durchführen.

List<Type> Os = ....;
Os.Auflisten().ForEach(
    delegate(Type o) {
        if(!o.cond) Os.Remove(o);
    }
);

Lösung - regulär foreach:

Diese Technik funktioniert auch für regelmäßig foreach Aussagen.

List<Type> Os = ....;
foreach(Type o in Os.Auflisten()) {
  if(!o.cond) Os.Remove(o);
}

Bitte beachten Sie, dass diese Lösung nicht funktioniert, wenn Ihre ursprüngliche Liste enthält struct Element.

Ich weiß, dass Sie nach etwas anderem gefragt haben, aber wenn Sie eine Reihe von Elementen bedingt entfernen möchten, können Sie den Lambda -Ausdruck verwenden:

Os.RemoveAll(o => !o.cond);
 Os.RemoveAll(delegate(int x) { return /// });

Ich würde versuchen, den Index des ersten Elements zu finden, der das Prädikat nicht erfüllt und Entfernung (0, Index) darauf ausführt. Wenn nichts anderes, sollte weniger Anrufe entfernt werden.

Update: Für die Vollständigkeit hinzugefügt

Wie mehrere geantwortet haben, sollten Sie eine Sammlung nicht ändern, während Sie sie mit Getenumerator () (Beispiel "iteriert haben foreach). Das Rahmen hindert Sie daran, dies zu tun, indem Sie eine Ausnahme auswerfen. Die generische Colution dazu besteht darin, "manuell" mit zu iterieren for (Siehe andere Antworten). Seien Sie vorsichtig mit Ihrem Index, sodass Sie keine Elemente überspringen oder dieselbe zweimal neu bewerten (unter Verwendung i-- oder rückwärts iterierend).

Für Ihren spezifischen Fall können wir jedoch die unten stehenden Vorgänge (en) optimieren ... Original -Antwort unten.


Wenn Sie alle Elemente entfernen möchten, bis man eine bestimmte Bedingung erfüllt (das ist, was Ihr Code tut), können Sie dies tun:

bool exitCondition;

while(list.Count > 0 && !(exitCondition = list[0].Condition))
   list.RemoveAt(0);

Oder wenn Sie eine einzelne Entfernenoperation verwenden möchten:

SomeType exitCondition;
int index = list.FindIndex(i => i.Condition);

if(index < 0)
    list.Clear();
else
{
    exitCondition = list[0].State;
    list.RemoveRange(0, count);
}

Hinweis: Da ich das nehme item.Condition ist bool, Ich benutze item.State Um den Ausgangszustand zu speichern.

Update: Die Begrenzungsbedingung und Speichern von Begrenzungsbedingungen für beide Beispiele hinzugefügt

Sie können es mit Linq tun

MyList = MyList.Where(x=>(someCondition(x)==true)).ToList()

If you know your list isn't very large you can use

foreach (Type o in new List<Type>(Os))
    ....

which will create a temporary duplicate of the list. Your remove() call will then not be interfering with the iterator.

Look at Enumerable.SkipWhile()

Enumerable.SkipWhile( x => condition).ToList()

Generally not mutating a list, makes live a lot easier. :)

There is a good discussion of this in Removing items in a list while iterating through it .

They propose:

for(int i = 0; i < count; i++)
{
    int elementToRemove = list.Find(<Predicate to find the element>);

    list.Remove(elementToRemove);
}

Anzurio's solution is probably the most straightforward, but here's another clean one, if you don't mind adding a bunch of interfaces/classes to your utilities library.

You can write it like this

List<Type> Os;
...
var en = Os.GetRemovableEnumerator();
while (en.MoveNext())
{
    if (en.Current.Cond)
        en.Remove();
}

Put the following infrastructure, inspired by Java's Iterator<T>.remove, into your utility library:

static class Extensions
{
    public static IRemovableEnumerator<T> GetRemovableEnumerator<T>(this IList<T> l)
    {
        return new ListRemovableEnumerator<T>(l);
    }
}

interface IRemovableEnumerator<T> : IEnumerator<T>
{
    void Remove();
}

class ListRemovableEnumerator<T> : IRemovableEnumerator<T>
{
    private readonly IList<T> _list;
    private int _count;
    private int _index;
    public ListRemovableEnumerator(IList<T> list)
    {
        _list = list;
        _count = list.Count;
        _index = -1;
    }

    private void ThrowOnModification()
    {
        if (_list.Count != _count)
            throw new InvalidOperationException("List was modified after creation of enumerator");
    }
    public void Dispose()
    {
    }

    public bool MoveNext()
    {
        ThrowOnModification();
        if (_index + 1 == _count)
            return false;
        _index++;
        return true;
    }

    public void Reset()
    {
        ThrowOnModification();
        _index = -1;
    }

    object IEnumerator.Current
    {
        get { return Current; }
    }

    public T Current
    {
        get { return _list[_index]; }
    }

    public void Remove()
    {
        ThrowOnModification();
        _list.RemoveAt(_index);
        _index--;
        _count--;
    }
}

I just had the same problem and solved it by using the following:

foreach (Type o in (new List(Os))) { if (something) Os.Remove(o); }

It iterates through a copy of the list and removes from the original list.

Add the item to remove in a list, and then remove these items by using RemoveAll:

List<Type> Os;
List<Type> OsToRemove=new List<Type>();
...
foreach (Type o in Os){
    if (o.cond)
        return;
    else
        OsToRemove.Add(o);
}
Os.RemoveAll(o => OsToRemove.Contains(o));
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top