Czy ktoś może mi wyjaśnić IEnumerable i IEnumerator?
na przykład, kiedy użyć go za foreach? Jaka jest różnica między IEnumerable i IEnumerator? Dlaczego musimy go używać?
c#
ienumerable
ienumerator
prodev42
źródło
źródło
Odpowiedzi:
Nie używasz
IEnumerable
„ponad”foreach
. WykonawczeIEnumerable
sprawia, że korzystanieforeach
to możliwe .Kiedy piszesz kod taki jak:
jest funkcjonalnie równoważny z pisaniem:
Przez „funkcjonalnie równoważny” mam na myśli właśnie to, w co kompilator zamienia kod. Nie można używać
foreach
nabaz
w tym przykładzie chybabaz
narzędziIEnumerable
.IEnumerable
oznacza, żebaz
implementuje metodęIEnumerator
Obiektem że metoda ta zwraca musi implementować metodyi
Pierwsza metoda przechodzi do następnego obiektu w
IEnumerable
obiekcie, który utworzył moduł wyliczający, zwracając,false
jeśli jest zrobiony, a druga zwraca bieżący obiekt.Wszystko w .Net, które można iterować po implementacjach
IEnumerable
. Jeśli budujesz własną klasę, która nie dziedziczy już po klasie, która się implementujeIEnumerable
, możesz uczynić ją użyteczną wforeach
instrukcjach, implementującIEnumerable
(i tworząc klasę wyliczającą,GetEnumerator
która zwróci jej nowa metoda).źródło
bool MoveNext()
metodę iCurrent
właściwość dowolnego typu. To podejście „typowania kaczego” zostało zaimplementowane w C # 1.0 jako sposób uniknięcia boksu podczas wyliczania kolekcji typów wartości.iterator
?Interfejsy IEnumerable i IEnumerator
Aby rozpocząć badanie procesu wdrażania istniejących interfejsów .NET, najpierw przyjrzyjmy się roli IEnumerable i IEnumerator. Przypomnij sobie, że C # obsługuje słowo kluczowe o nazwie foreach, które pozwala iterować po zawartości dowolnego typu tablicy:
Chociaż może się wydawać, że tylko typy tablic mogą korzystać z tego konstruktu, prawda jest taka, że każdy typ obsługujący metodę o nazwie GetEnumerator () może zostać oceniony przez konstrukcję foreach. Aby to zilustrować, podążaj za mną!
Załóżmy, że mamy klasę Garaż:
Idealnie byłoby iterować podelementy obiektu Garage za pomocą konstrukcji foreach, podobnie jak tablica wartości danych:
Niestety kompilator informuje, że klasa Garage nie implementuje metody o nazwie GetEnumerator (). Ta metoda jest sformalizowana przez interfejs IEnumerable, który znajduje się w przestrzeni nazw System.Collections. Klasy lub struktury, które obsługują to zachowanie, reklamują, że są w stanie udostępnić wywołującym zawarte elementy podrzędne (w tym przykładzie samo słowo kluczowe foreach). Oto definicja tego standardowego interfejsu .NET:
Jak widać, metoda GetEnumerator () zwraca odwołanie do jeszcze innego interfejsu o nazwie System.Collections.IEnumerator. Ten interfejs zapewnia infrastrukturę umożliwiającą rozmówcy przechodzenie przez wewnętrzne obiekty zawarte w kontenerze kompatybilnym z IEnumerable:
Jeśli chcesz zaktualizować typ garażu, aby obsługiwał te interfejsy, możesz przejść długą drogę i zaimplementować każdą metodę ręcznie. Chociaż z pewnością masz swobodę dostarczania niestandardowych wersji GetEnumerator (), MoveNext (), Current i Reset (), istnieje prostszy sposób. Ponieważ typ System.Array (jak również wiele innych klas kolekcji) już implementuje IEnumerable i IEnumerator, możesz po prostu delegować żądanie do System.Array w następujący sposób:
Po zaktualizowaniu typu garażu możesz bezpiecznie używać go w konstrukcji Cach foreach. Ponadto, biorąc pod uwagę, że metoda GetEnumerator () została zdefiniowana publicznie, użytkownik obiektu może również wchodzić w interakcje z typem IEnumerator:
Jeśli jednak wolisz ukryć funkcjonalność IEnumerable na poziomie obiektu, po prostu skorzystaj z jawnej implementacji interfejsu:
W ten sposób przypadkowy użytkownik obiektu nie znajdzie metody GarEnumerator () Garage, podczas gdy konstrukcja foreach uzyska interfejs w tle, gdy będzie to konieczne.
Na podstawie Pro C # 5.0 i .NET 4.5 Framework
źródło
Garage
który pobieracarArray
? W ten sposób nie musisz się wdrażaćGetEnumerator
, ponieważArray
już to robi. Egforeach(Car c in carLot.getCars()) { ... }
Implementacja IEnumerable oznacza, że twoja klasa zwraca obiekt IEnumerator:
Wdrożenie IEnumerator oznacza, że klasa zwraca metody i właściwości dla iteracji:
To i tak różnica.
źródło
Objaśnienie za pomocą Analogii + przewodnik po kodzie
Najpierw wyjaśnienie bez kodu, a następnie dodam je później.
Powiedzmy, że prowadzisz linię lotniczą. I w każdym samolocie chcesz znać informacje o pasażerach lecących samolotem. Zasadniczo chcesz być w stanie „przemierzać” samolot. Innymi słowy, chcesz móc zacząć na przednim siedzeniu, a następnie skierować się do tyłu samolotu, prosząc pasażerów o informacje: kim oni są, skąd pochodzą itp. Samolot może to zrobić tylko , Jeśli to jest:
Dlaczego te wymagania? Ponieważ tego wymaga interfejs.
Jeśli jest to przeciążenie informacją, wystarczy wiedzieć, że chcesz zadać każdemu pasażerowi w samolocie kilka pytań, zaczynając od pierwszego do ostatniej.
Co znaczy liczyć?
Jeśli linia lotnicza jest „policzalna”, oznacza to, że MUSI być obecny stewardesa w samolocie, którego jedynym zadaniem jest liczyć - a stewardesa MUSI liczyć w bardzo szczególny sposób:
Procedury liczenia
Kapitan linii lotniczej chce sporządzić raport na temat każdego pasażera w momencie, gdy zostaną zbadani lub policzeni. Tak więc po rozmowie z osobą na pierwszym miejscu stewardesa / licznik następnie zgłasza się do kapitana, a gdy przekazany jest raport, licznik pamięta swoją dokładną pozycję w przejściu i kontynuuje liczenie od miejsca, w którym wyszedł poza.
W ten sposób kapitan zawsze może uzyskać informacje o bieżącej osobie, która jest przesłuchiwana. W ten sposób, jeśli dowie się, że ta osoba lubi Manchester City, może udzielić pasażerowi preferencyjnego traktowania itp.
Połączmy to z IEnumerables
Wyliczenie to tylko zbiór pasażerów w samolocie. Prawo o lotnictwie cywilnym - są to w zasadzie zasady, których muszą przestrzegać wszystkie numery IE. Za każdym razem, gdy stewardesa podchodzi do kapitana z informacją pasażera, w zasadzie „oddajemy” pasażera kapitanowi. Kapitan może w zasadzie robić wszystko, co chce z pasażerem - z wyjątkiem zmiany rozmieszczenia pasażerów w samolocie. W takim przypadku są traktowani preferencyjnie, jeśli podążają za Manchester City (ugh!)
Podsumowanie
Innymi słowy, coś można policzyć, jeśli ma licznik . I licznik musi (w zasadzie): (i) zapamiętać swoje miejsce ( stan ), (ii) móc przejść dalej , (iii) i wiedzieć o obecnej osobie, z którą ma do czynienia.
Wyliczenie to tylko wymyślne słowo na „policzalny”. Innymi słowy, wyliczalny pozwala „wyliczyć” (tj. Policzyć).
źródło
IEnumerable implementuje GetEnumerator. Po wywołaniu ta metoda zwróci IEnumerator, który implementuje MoveNext, Reset i Current.
Tak więc, gdy twoja klasa implementuje IEnumerable, mówisz, że możesz wywołać metodę (GetEnumerator) i otrzymać nowy obiekt (IEnumerator), którego możesz użyć w pętli, takiej jak foreach.
źródło
Implementacja IEnumerable umożliwia uzyskanie IEnumeratora dla listy.
IEnumerator pozwala na sekwencyjny dostęp do stylu foreach dla elementów na liście przy użyciu słowa kluczowego fed.
Przed implementacją foreach (na przykład w Javie 1.4) sposobem na iterację listy było pobranie enumeratora z listy, a następnie poproszenie go o pozycję „następną” na liście, o ile wartość zwracana jest jako następna pozycja nie jest pusta. Foreach robi to po prostu niejawnie jako funkcję języka, w taki sam sposób, w jaki lock () implementuje klasę Monitor za scenami.
Spodziewam się, że foreach działa na listach, ponieważ implementują IEnumerable.
źródło
Pomyśl o obiektach policzalnych jak o listach, stosach, drzewach.
źródło
IEnumerable i IEnumerator (i ich ogólne odpowiedniki IEnumerable <T> i IEnumerator <T>) są podstawowymi interfejsami implementacji iteratora w kolekcjach Libray klasy .Net Framework .
IEnumerable to najczęstszy interfejs, który można zobaczyć w większości kodu. Umożliwia pętlę foreach, generatory (myśl wydajność ), a ze względu na mały interfejs służy do tworzenia ścisłych abstrakcji. IEnumerable zależy od IEnumerator .
Z drugiej strony IEnumerator zapewnia interfejs iteracji nieco niższego poziomu. Jest to określane jako jawny iterator, który daje programiście większą kontrolę nad cyklem iteracji.
IEnumerable
IEnumerable to standardowy interfejs, który umożliwia iterację po kolekcjach, które go obsługują (w rzeczywistości wszystkie typy kolekcji, o których dziś myślę, implementują IEnumerable ). Obsługa kompilatora umożliwia takie funkcje językowe jak
foreach
. Ogólnie rzecz biorąc, umożliwia to niejawną implementację iteratora .foreach Loop
Myślę, że
foreach
pętla jest jednym z głównych powodów używania interfejsów IEnumerable .foreach
ma bardzo zwięzłą składnię i jest bardzo łatwy do zrozumienia w porównaniu do klasycznego stylu C dla pętli, w których musisz sprawdzić różne zmienne, aby zobaczyć, co robi.wydajność Słowo kluczowe
Prawdopodobnie mniej znaną cechą jest to, że IEnumerable włącza również generatory w języku C # za pomocą instrukcji
yield return
iyield break
.Abstrakcje
Innym częstym scenariuszem w praktyce jest użycie IEnumerable w celu zapewnienia minimalistycznych abstrakcji. Ponieważ jest to niewielki interfejs i tylko do odczytu, zachęca się do ujawnienia swoich kolekcji jako IEnumerable (zamiast na przykład List ). W ten sposób możesz swobodnie zmieniać swoją implementację bez łamania kodu klienta ( na przykład zmień List na LinkedList ).
Gotcha
Jednym z zachowań, o których należy pamiętać, jest to, że w implementacjach przesyłania strumieniowego (np. Pobieranie danych wiersz po rzędzie z bazy danych, zamiast ładowania wszystkich wyników do pamięci w pierwszej kolejności) nie można iterować kolekcji więcej niż jeden raz. Jest to sprzeczne z kolekcjami w pamięci, takimi jak Lista , w których można iterować wiele razy bez problemów. Na przykład ReSharper ma kontrolę kodu pod kątem możliwego wielokrotnego wyliczenia IEnumerable .
IEnumerator
Z drugiej strony IEnumerator to interfejs, który sprawia, że IEnumerble-foreach-magic działa. Ściśle mówiąc, umożliwia jawne iteratory.
Z mojego doświadczenia wynika, że IEnumerator jest rzadko używany w typowych scenariuszach ze względu na bardziej szczegółową składnię i nieco mylącą semantykę (przynajmniej dla mnie; np. MoveNext () również zwraca wartość, której nazwa wcale nie sugeruje).
Przypadek użycia dla IEnumerator
Korzystałem tylko z bibliotek IEnumerator, w szczególności (nieco niższego poziomu) bibliotek i frameworków, w których zapewniałem interfejsy IEnumerable . Jednym z przykładów jest biblioteka przetwarzania strumienia danych, która zapewniała szereg obiektów w
foreach
pętli, mimo że za kulisami dane były zbierane przy użyciu różnych strumieni plików i serializacji.Kod klienta
Biblioteka
źródło
Implementacja
IEnumerable
zasadniczo oznacza, że obiekt może być iterowany. Nie musi to oznaczać, że jest to tablica, ponieważ istnieją pewne listy, których nie można indeksować, ale można je wyliczyć.IEnumerator
jest rzeczywistym obiektem używanym do wykonywania iteracji. Kontroluje przechodzenie od jednego obiektu do drugiego na liście.Przez większość czasu
IEnumerable
&IEnumerator
są używane w sposób przezroczysty jako częśćforeach
pętli.źródło
Różnice między IEnumerable i IEnumerator:
Ilekroć przekazujemy kolekcję IEnumerable do innej funkcji, nie zna ona aktualnej pozycji elementu / obiektu (nie wie, który element wykonuje)
IEnumerable ma jedną metodę GetEnumerator ()
IEnumerator ma jedną właściwość o nazwie Bieżąca i dwie metody: Reset () i MoveNext () (co jest przydatne do poznania aktualnej pozycji elementu na liście).
źródło
IEnumerable to pole zawierające Ienumerator. IEnumerable to podstawowy interfejs dla wszystkich kolekcji. Pętla foreach może działać, jeśli kolekcja implementuje IEnumerable. W poniższym kodzie wyjaśniono etap posiadania własnego modułu wyliczającego. Najpierw określmy naszą klasę, której kolekcję będziemy tworzyć.
Teraz zdefiniujemy klasę, która będzie działać jako kolekcja dla naszego klienta klasy. Zauważ, że implementuje interfejs IEnumerable. Dlatego musimy wdrożyć metodę GetEnumerator. Zwróci to nasz niestandardowy moduł wyliczający.
Teraz stworzymy własny niestandardowy moduł wyliczający w następujący sposób. Musimy więc zaimplementować metodę MoveNext.
Teraz możemy użyć pętli foreach w naszej kolekcji, jak poniżej;
źródło
Zrozumienie wzoru Iteratora będzie dla ciebie pomocne. Polecam przeczytać to samo.
Wzór iteratora
Na wysokim poziomie wzorzec iteratora może być używany do zapewnienia standardowego sposobu iteracji przez kolekcje dowolnego typu. Mamy 3 uczestników wzorca iteratora, faktycznej kolekcji (klienta), agregatora i iteratora. Agregat to klasa interfejsu / abstrakcyjna, która ma metodę zwracającą iterator. Iterator to klasa interfejsu / abstrakcyjna, która ma metody pozwalające na iterację w kolekcji.
Aby zaimplementować wzorzec, musimy najpierw zaimplementować iterator, aby uzyskać konkretny element, który może iterować po danej kolekcji (kliencie). Następnie kolekcja (klient) implementuje agregator, aby zwrócić instancję powyższego iteratora.
Oto schemat UML
Więc w zasadzie w c #, IEnumerable jest abstrakcyjnym agregatem, a IEnumerator jest abstrakcyjnym Iteratorem. IEnumerable ma jedną metodę GetEnumerator, która jest odpowiedzialna za utworzenie instancji IEnumerator żądanego typu. Kolekcje takie jak Listy implementują IEnumerable.
Przykład. Załóżmy, że mamy metodę,
getPermutations(inputString)
która zwraca wszystkie permutacje łańcucha i że metoda zwraca instancjęIEnumerable<string>
Aby policzyć liczbę permutacji, możemy zrobić coś takiego jak poniżej.
Kompilator c # mniej więcej konwertuje powyższe na
Jeśli masz jakieś pytania, nie wahaj się zadać.
źródło
Niewielki wkład.
Ponieważ wiele z nich wyjaśnia „kiedy używać” i „używać z foreach”. Pomyślałem o dodaniu tutaj innej różnicy stanów, o co pytano w pytaniu o różnicę między IEnumerable IEnumerator.
Utworzyłem poniższy przykład kodu na podstawie poniższych wątków dyskusji.
IEnumerable, IEnumerator vs foreach, kiedy używać Jaka jest różnica między IEnumerator a IEnumerable?
Moduł wyliczający zachowuje stan (pozycję iteracji) między wywołaniami funkcji, podczas gdy iteracje z drugiej strony nie.
Oto przetestowany przykład z komentarzami do zrozumienia.
Eksperci, dodaj mnie / popraw mnie.
źródło
Zauważyłem te różnice:
Odp .: Iterujemy listę w inny sposób, foreach może być użyty dla IEnumerable, a while dla IEnumerator.
B. IEnumerator może zapamiętać bieżący indeks, kiedy przechodzimy z jednej metody do drugiej (zaczyna działać z bieżącym indeksem), ale IEnumerable nie może zapamiętać indeksu i resetuje indeks do początku. Więcej w tym wideo https://www.youtube.com/watch?v=jd3yUjGc9M0
źródło
IEnumerable
iIEnumerator
oba są interfejsami w języku C #.IEnumerable
to interfejs definiujący pojedynczą metodęGetEnumerator()
zwracającąIEnumerator
interfejs.Działa to w przypadku dostępu tylko do odczytu do kolekcji, której implementacje
IEnumerable
mogą być używane zforeach
instrukcją.IEnumerator
ma dwie metodyMoveNext
iReset
. Ma również właściwość o nazwieCurrent
.Poniżej przedstawiono implementację IEnumerable i IEnumerator.
źródło
źródło