2009-07-06 17 views
22

przechodziłem przez IEnumerable i IEnumerator, ale nie mogłem uzyskać jednego punktu wyraźnie .. jeśli mamy foreach, to dlaczego potrzebujemy tych dwóch interfejsów? Czy jest jakikolwiek scenariusz, w którym musimy używać interfejsów. Jeśli tak, to ktoś może wyjaśnić na przykładzie. Wszelkie sugestie i uwagi są mile widziane. Dzięki. W wielu przypadkach interfejsówIEnumerable, IEnumerator vs foreach, kiedy używać tego, co

Odpowiedz

46

foreachużywa interfejsów. Potrzebujesz interfejsów, aby implementować sekwencję, którą następnie można użyć. (Bloki Iterator zazwyczaj bardzo ułatwiają to zadanie implementacyjne.)

Jednak, po prostu okazjonalnie może być przydatne użycie iteratorów bezpośrednio. Dobrym przykładem jest próba "powiązania" dwóch różnych sekwencji. Załóżmy na przykład, że otrzymujesz dwie sekwencje - jedną z nazw, jedną z przedziałów wiekowych i chcesz wydrukować te dwie razem. można napisać:

static void PrintNamesAndAges(IEnumerable<string> names, IEnumerable<int> ages) 
{ 
    using (IEnumerator<int> ageIterator = ages.GetEnumerator()) 
    { 
     foreach (string name in names) 
     { 
      if (!ageIterator.MoveNext()) 
      { 
       throw new ArgumentException("Not enough ages"); 
      } 
      Console.WriteLine("{0} is {1} years old", name, ageIterator.Current); 
     } 
     if (ageIterator.MoveNext()) 
     { 
      throw new ArgumentException("Not enough names"); 
     } 

    } 
} 

Podobnie może być przydatna w użyciu iteracyjnej jeśli chcesz traktować (powiedzmy) pierwszy element odmiennie do reszty:

public T Max<T>(IEnumerable<T> items) 
{ 
    Comparer<T> comparer = Comparer<T>.Default; 

    using (IEnumerator<T> iterator = items.GetEnumerator()) 
    { 
     if (!iterator.MoveNext()) 
     { 
      throw new InvalidOperationException("No elements"); 
     } 
     T currentMax = iterator.Current; 

     // Now we've got an initial value, loop over the rest 
     while (iterator.MoveNext()) 
     { 
      T candidate = iterator.Current; 
      if (comparer.Compare(candidate, currentMax) > 0) 
      { 
       currentMax = candidate; 
      } 
     } 
     return currentMax; 
    } 
} 

Teraz, jeśli "interesuje mnie różnica między IEnumerator<T> i IEnumerable<T>, możesz myśleć o niej w kategoriach baz danych: pomyśl o IEnumerable<T> jako tabeli i IEnumerator<T> jako kursorze. Możesz poprosić tabelę o nowy kursor, a jednocześnie możesz mieć wiele kursorów nad tym samym stołem.

Potrącenie tej różnicy może zająć trochę czasu, ale wystarczy pamiętać, że lista (lub tablica, lub cokolwiek innego) nie ma pojęcia "gdzie jesteś na liście", ale jest iteratorem na tej liście/array/niezależnie od tego, czy ma mieć ten stan jest pomocne.

+0

Dziękuję Jonowi za całą pomoc. Mam to teraz i będę uczyć się więcej, aby było jasne. Również znajduję następujący link bardzo użyteczny: http://stackoverflow.com/questions/558304/can-anyone-explain-ienumerable-and-ienumerator-to-me – Wondering

+0

To jest przykład miejsca, w którym utknąłeś przy użyciu IEnumeratora aby pozostać wydajnym. http://stackoverflow.com/questions/1018407/what-jest-najwyklej-elegant-way-do-get-of-items-by-index-from--collection/1025137#1025137 –

+0

@ Jon Skeet Bardzo podobał mi się sposób, w jaki zakodowałeś metodę Max . Mam jedno pytanie. użyłeś używając konstrukcji z IEnumberator. Wiem, że używanie utylizuje to, co się w nim znajduje(). Dlaczego użyłeś go na IEnumberator? –

8

Co Jon powiedział.

  • IEnumerable lub IEnumerable<T>: wdrażając ten obiekt stwierdza, że ​​może dać Ci iterator, którego można użyć do przechodzenia przez sekwencję/kolekcji/set
  • IEnumerator lub IEnumerator<T>: jeśli wywołanie metody GetEnumerator zdefiniowanego w poprzednim interfejsie otrzymujesz obiekt iteratora jako odniesienie do IEnumeratora. Umożliwia to wywołanie metody MoveNext() i pobranie bieżącego obiektu.
  • foreach: jest konstrukcją/fasadą C# w pewnym sensie, że nie musisz wiedzieć, jak działa pod maską. Wewnętrznie pobiera iterator i wywołuje odpowiednie metody, aby skoncentrować się na tym, co chcesz zrobić z każdym elementem (zawartością bloku foreach). W większości przypadków potrzebujesz foreach, chyba że implementujesz własny typ lub niestandardową iterację, w takim przypadku musisz poznać dwa pierwsze interfejsy.
+0

Dzięki Gishu za uczynienie go bardziej zrozumiałym dla mnie. również uważam, że ten link jest bardzo użyteczny: http://stackoverflow.com/questions/558304/can-anyone-explain-ienumerable-and-ienumerator-to-me – Wondering

6

pamiętać, że nie mieć wdrożenia IEnumerator i ich varients używać foreach - IIRC, wszystko to musi to metoda GetEnumerator(), która zwraca obiekt, który ma metodę MoveNext() przekazujących a bool i właściwość Current zwracająca obiekt. Nie musisz używać IEnumeratora i IEnumerable, chociaż generalnie jest to bardzo dobry pomysł. Aby uzyskać więcej informacji, patrz here.

+0

To dziwna, stara bestia, ta - możesz po prostu zastosuj dwie metody. Dużym problemem jest to, że system typów nie wie, że rzecz jest wymienna, więc wyobrażam sobie, że miałbyś problem z jej użyciem, na przykład, z Linq do Objects. –

+3

Tak, to jest dziwne. Powodem, dla którego wspieramy tę funkcję jest to, że możesz napisać zbiór liczb całkowitych w C# 1.0 i wyliczyć je bez boksowania. Teraz, gdy mamy IE , nie ma już potrzeby stosowania podejścia "wzorcowego". –

+1

@Eric Lippert: Jest jeszcze jedna zaleta tej cechy: pozwala uniknąć alokacji sterty dla enumeratorów strukturalnych, choć sugerowałbym, że klasy, których metody GetEnumerator zwrócą strukturę, powinny mieć metody IEnumerable.GetEnumerator, które zwracają klasy. – supercat

Powiązane problemy