2011-01-01 11 views
17

Jak ponownie uruchomić pętlę foreach w języku C#?Uruchom ponownie pętlę foreach w C#?

Na przykład:

Action a; 
foreach(Constrain c in Constrains) 
{ 
    if(!c.Allows(a)) 
    { 
     a.Change(); 
     restart; 
    } 
} 

restart tutaj jest jak continue lub break ale restartuje foreach od początku To jak ustawienie licznika od a for pętla do 0 ponownie ..

Czy to możliwe w C#?

Edit: Chciałbym podziękować zarówno Mehrdad Afshari i Mahesh Velaga pozwoliłeś mi odkryć błąd (indeks = 0) w mojej obecnej implementacji, które nie zostały odkryte inaczej ..

+0

To może być interesujące wiedzieć, gdzie dokładnie trzeba użyć tego rodzaju restartu. Używasz listy zmiennych obiektów w jakimś algorytmie. Czy możesz podzielić się rzeczywistym problemem, który próbujesz rozwiązać? –

+0

Rzeczywisty problem: niektórzy agenci próbują się poruszać w środowisku. W env są bariery. Po tym, jak każdy agent zdecyduje, co oznacza następne działanie, środowisko sprawdza, czy agent przekroczy barierę, jeśli tak, środowisko pozwala agentowi wybrać inne działanie; i tutaj, gdzie muszę ponownie uruchomić pętlę foreach, aby ponownie sprawdzić wszystkie bariery z nowo wybraną akcją. Mam nadzieję, że to wyjaśnia ... – Betamoo

Odpowiedz

53

Korzystając stare dobre goto :

restart: 
foreach(Constrain c in Constrains) 
{ 
    if(!c.Allows(a)) 
    { 
     a.Change(); 
     goto restart; 
    } 
} 

Jeśli zdiagnozowano gotophobia 100% czasu z jakiegoś powodu (co jest nie dobrą rzeczą bez powodu), możesz spróbować użyć flagi zamiast:

bool restart; 
do { 
    restart = false; 
    foreach(Constrain c in Constrains) 
    { 
     if(!c.Allows(a)) 
     { 
     a.Change(); 
     restart = true; 
     break; 
     } 
    } 
} while (restart); 
+3

+1 Podoba mi się twoja implementacja pętli do. –

+8

Należy zachować ostrożność przy dostawach http://xkcd.com/292/ – digEmAll

+1

uważać! w dotnet3.5 i poza nią (LINQ itp.) foreach() może ściągnąć zamknięcie. To rozwiązanie lub jakikolwiek inny, który wyskakuje z pętli, prawidłowo zamknie zamknięcie. Ale przemyśleć to; jest to miejsce, w którym ostrożny projekt pozwoli zaoszczędzić sporo debugowania. –

4
void Main() 
{ 
    IEnumerable<Constrain> cons; 
    SomeObject a; 

    while(!TryChangeList(cons, a)) { } 
} 

// the name tryChangeList reveals the intent that the list will be changed 
private bool TryChangeList(IEnumerable<Constrain> constrains, SomeObject a) 
{ 
    foreach(var con in constrains) 
    { 
     if(!c.Allows(a)) 
     { 
      a.Change(); 
      return false; 
     } 
    } 
    return true; 
} 
+0

+1. Refaktoryzacja jest zwykle dobrym rozwiązaniem w tego rodzaju przypadkach, jeśli jest to wykonalne i nie czyni bardziej skomplikowanym. –

+1

-1 co jeśli nie jest to "IList "? –

+0

@John - to zadziała dla IEnumerable również, jeśli zmienisz IList na IEnumerable –

8

Jednym ze sposobów można to zrobić jest użycie do, jak już wspomniano:

restart tutaj jest jak kontynuować lub przerwać ale restartuje foreach z Początki Jest jak ustawienie licznika pętli for ponownie do 0

Action a; 
for(var index = 0; index < Constratins.Count; index++) 
{ 
    if(!Constraints[index].Allows(a)) 
    { 
     a.Change(); 
     index = -1; // restart 
    } 
} 
+7

To jest złe. 'index = -1' zadziałałoby, gdyby przeliczalne było dostępne dla indeksu, ale nawet w tym przypadku utrudnia odczytanie kodu i sprawia, że ​​intencja jest niejasna. Jest to doskonały przykład robienia rzeczy gorszych w wyniku ślepej gotofobii. Przynajmniej z 'goto', cel jest całkowicie jasny. –

+0

+1 To pierwsze rozwiązanie, o którym myślałem - Dlaczego nie użyć pętli for? Po co tworzyć skandaliczne rozwiązanie foreach. –

+0

@Mehrdad: poprawiono część indeksu. Dzięki –

0
for (var en = Constrains.GetEnumerator(); en.MoveNext();) 
{ 
    var c = en.Current; 
    if (!c.Allows(a)) 
    { 
     a.Change(); 
     en = Constrains.GetEnumerator(); 
    } 
} 
+4

Najpierw zechcesz wyrzucić oryginał. –

+3

@John: Rzeczywiście. I musisz poradzić sobie z potencjalnymi wyjątkami, ... co w końcu prowadzi do stwierdzenia "używanie" iw tym momencie uświadomisz sobie, że nie powinieneś był porzucać 'foreach' w pierwszej kolejności! –

8

Chociaż bardzo starym wątku - żadna z odpowiedzi płatnych należytej uwagi do semantyki tego kodu:

  • masz łańcuch ograniczeń na a
  • If a przerwy żadnej z nich, spróbuj innego a i popchnij to przez łańcuch.

Oznacza to, że a.Change() powinny być oddzielone od pętli więzów kontroli, także przestrzeganiu zasady CQS:

while (!MeetsConstraints(a)) 
{ 
    a.Change(); 
} 

bool MeetsConstraints(Thing a) 
{ 
    return Constraints.All(c => c.Allows(a)); 
} 

Nie goto żadne brzydkie pętle, tylko proste i czyste. </Samodzielne back-slapping >