Jak działają pętle foreach w C #? [Zamknięte]

86

Jakie typy klas mogą używać foreachpętli?

user48616
źródło
5
Możesz wybrać inną zaakceptowaną odpowiedź, ponieważ ta, którą wybrałeś, nie jest reprezentatywna dla poprawnej odpowiedzi.
George Stocker

Odpowiedzi:

162

Właściwie, ściśle rzecz biorąc, wszystko, czego potrzebujesz, foreachto GetEnumerator()metoda publiczna , która zwraca coś z bool MoveNext()metodą i ? Current {get;}właściwością. Jednak najczęstszym znaczeniem tego jest „coś, co implementuje IEnumerable/ IEnumerable<T>, zwracając IEnumerator/ IEnumerator<T>.

W sposób dorozumiany, obejmuje to wszystko, narzędzia ICollection/ ICollection<T>, jak coś podobnego Collection<T>, List<T>tablice ( T[]), itd. Tak więc każdego standardowego „zbiór danych” będzie generalnie wspierać foreach.

W celu potwierdzenia pierwszego punktu, poniższe działa dobrze:

using System;
class Foo {
    public int Current { get; private set; }
    private int step;
    public bool MoveNext() {
        if (step >= 5) return false;
        Current = step++;
        return true;
    }
}
class Bar {
    public Foo GetEnumerator() { return new Foo(); }
}
static class Program {
    static void Main() {
        Bar bar = new Bar();
        foreach (int item in bar) {
            Console.WriteLine(item);
        }
    }
}

Jak to działa?

Każda pętla, taka jak foreach(int i in obj) {...}rodzaj, równa się:

var tmp = obj.GetEnumerator();
int i; // up to C# 4.0
while(tmp.MoveNext()) {
    int i; // C# 5.0
    i = tmp.Current;
    {...} // your code
}

Istnieją jednak różnice. Na przykład, jeśli moduł wyliczający (tmp) obsługuje IDisposable, jest również używany (podobnie jak using).

Zwróć uwagę na różnicę w umieszczaniu deklaracji „ int iwewnątrz (C # 5,0) w porównaniu z zewnętrzną (w C # 4,0) pętli. Jest to ważne, jeśli używasz ianonimowej metody / lambdy wewnątrz bloku kodu. Ale to już inna historia ;-p

Marc Gravell
źródło
3
+1 za pogłębienie. Zwykle nie zagłębiam się w pytanie, które może być pytaniem dla początkujących, ponieważ dla nowego programisty wydawałoby się to przytłaczające.
George Stocker
To prawda, Gortok - więc poszedłem do rzeczy o listach / tablicach / itd.
Marc Gravell
Dobrze byłoby wspomnieć o niejawnym rzutowaniu środowiska uruchomieniowego na zmienną pętli - może generować wyjątki niezgodności typów.
Daniel Earwicker,
3
Nie zapomnij: Używając tablicy z foreach, kompilator tworzy prosty for-loop(widać to podczas pracy z IL).
Felix K.
1
@Marc Gravell: OK, super! Zredagowałem post, aby był bardziej zrozumiały - przynajmniej dla mnie. W końcu umiejscowienie jest nie tylko ważne w C # 5.0, ale jest zawsze ważne, tylko że się zmieniło. Mam nadzieję, że nie masz nic przeciwko.
Paul Groke
7

Z MSDN :

foreachStwierdzenie powtarza grupę wbudowane instrukcje dla każdego elementu macierzy lub zbiór obiektów . foreachOświadczenie jest używany do iteracji kolekcji, aby uzyskać żądaną informację, ale nie powinny być wykorzystywane do zmiany zawartości kolekcji uniknąć nieprzewidywalnych skutków ubocznych. (podkreślenie moje)

Więc jeśli masz tablicę, możesz użyć instrukcji foreach do iteracji po tablicy, na przykład:

 int[] fibarray = new int[] { 0, 1, 2, 3, 5, 8, 13 };
    foreach (int i in fibarray)
    {
        System.Console.WriteLine(i);
    }

Możesz go również użyć do iteracji w List<T>kolekcji, na przykład:

List<string> list = new List<string>();

foreach (string item in list)
{
    Console.WriteLine(item);
}
George Stocker
źródło
4
co dziwne, zgodnie z MSDN ( msdn.microsoft.com/en-us/library/9yb8xew9(VS.80).aspx ) typy obiektów nie muszą implementować IEnumerable. Każdy typ, który poprawnie definiuje GetEnumerator, MoveNext, Reset i Current będzie działał. Dziwne, co?
Sean Reilly
Schludny. Nie wiedziałem tego. :-)
George Stocker
2

Oto dokumentacja: Główny artykuł z tablicami z obiektami kolekcji

Należy zauważyć, że „Typ elementu kolekcji musi być możliwy do przekonwertowania na typ identyfikatora”. Czasami nie można tego sprawdzić w czasie kompilacji i może wygenerować wyjątek czasu wykonywania, jeśli typu wystąpienia nie można przypisać do typu referencyjnego.

Spowoduje to wygenerowanie wyjątku czasu wykonywania, jeśli w koszyku z owocami znajduje się produkt inny niż Apple, na przykład pomarańcza.

List<Fruit> fruitBasket = new List<Fruit>() { new Apple(), new Orange() };
foreach(Apple a in fruitBasket)

To bezpiecznie filtruje listę tylko do jabłek przy użyciu Enumerable.OfType

foreach(Apple a in fruitBasket.OfType<Apple>() )
Amy B.
źródło
-1
IList<ListItem> illi = new List<ListItem>();
ListItem li = null;

foreach (HroCategory value in listddlsubcategory)
{
    listddlsubcategoryext = server.getObjectListByColumn(typeof(HroCategory), "Parentid", value.Id);
    li = new ListItem();
    li.Text = value.Description;
    li.Value = value.Id.ToString();
    illi.Add(li);
    IList<ListItem> newilli = new List<ListItem>();
    newilli = SubCatagoryFunction(listddlsubcategoryext, "-->");
    foreach (ListItem c in newilli)
    {
        illi.Add(c);
    }
}
shahzad
źródło
-1

Przydatne informacje na ten temat można również znaleźć w witrynie MSDN . Biorąc esencję z tego artykułu:

Słowo kluczowe foreach wylicza kolekcję, wykonując instrukcję embedded raz dla każdego elementu w kolekcji:

foreach (var item in collection)
{
    Console.WriteLine(item.ToString());
}

Kompilator tłumaczy pętlę foreach pokazaną w powyższym przykładzie na coś podobnego do tej konstrukcji:

IEnumerator<int> enumerator = collection.GetEnumerator();
while (enumerator.MoveNext())
{
    var item = enumerator.Current;
    Console.WriteLine(item.ToString());
}
r23
źródło
-2

możesz spróbować tego ...

List<int> numbers = new List<int>();
        numbers.Add(5);
        numbers.Add(15);
        numbers.Add(25);
        numbers.Add(35);

        Console.WriteLine("You are added total number: {0}",numbers.Count);
        foreach (int number in numbers)
        {
            Console.WriteLine("Your adding Number are: {0}", number);
        }
Dedarul
źródło