Einfache Verwendung des IEnumerators (mit Beispiel)

73

Ich habe Probleme, mich daran zu erinnern, wie (aber nicht warum) IEnumerators in C # verwendet werden soll. Ich bin an Java mit seiner wunderbaren Dokumentation gewöhnt, die Anfängern alles sehr gut erklärt. Also bitte, ertrage es mit mir.

Ich habe erfolglos versucht, aus anderen Antworten auf diesen Boards zu lernen. Anstatt eine allgemeine Frage zu stellen, die bereits zuvor gestellt wurde, habe ich ein spezielles Beispiel, das die Dinge für mich klarstellen würde.

Angenommen, ich habe eine Methode, der ein IEnumerable<String>Objekt übergeben werden muss. Alles, was die Methode tun muss, ist, die Buchstaben roxxorsan das Ende jedes einzelnen Stringim Iterator zu verketten . Dieser neue Iterator wird dann zurückgegeben (natürlich IEnumerablebleibt das ursprüngliche Objekt unverändert ).

Wie würde ich das machen? Die Antwort hier sollte natürlich neben mir vielen bei grundlegenden Fragen zu diesen Objekten helfen.

BlackVegetable
quelle

Antworten:

104

Hier ist die Dokumentation zu IEnumerator. Sie werden verwendet, um die Werte von Listen abzurufen, bei denen die Länge nicht unbedingt im Voraus bekannt ist (obwohl dies möglich ist). Das Wort kommt von enumerate, was bedeutet "eins zählen oder eins nach dem anderen benennen".

IEnumeratorund IEnumerator<T>wird von allen IEnumerableund IEnumerable<T>Schnittstellen (letztere bieten beide) in .NET über bereitgestellt GetEnumerator(). Dies ist wichtig, da die foreachAnweisung so konzipiert ist, dass sie über diese Schnittstellenmethoden direkt mit Enumeratoren zusammenarbeitet.

Also zum Beispiel:

IEnumerator enumerator = enumerable.GetEnumerator();

while (enumerator.MoveNext())
{
    object item = enumerator.Current;
    // Perform logic on the item
}

Wird:

foreach(object item in enumerable)
{
    // Perform logic on the item
}

In Bezug auf Ihr spezifisches Szenario werden fast alle Sammlungen in .NET implementiert IEnumerable. Aus diesem Grund können Sie Folgendes tun:

public IEnumerator Enumerate(IEnumerable enumerable)
{
    // List implements IEnumerable, but could be any collection.
    List<string> list = new List<string>(); 

    foreach(string value in enumerable)
    {
        list.Add(value + "roxxors");
    }
    return list.GetEnumerator();
}
Paul Walls
quelle
Reset () wird von foreach nicht aufgerufen. Es ist für COM-Interop gedacht und wird nicht von allen Enumeratoren unterstützt (tatsächlich unterstützen es die meisten Enumeratoren nicht ).
R. Martinho Fernandes
Enumeratoren sind schreibgeschützt, richtig? Wie kann ich einen Enumeratortyp mit meinen Änderungen unter Verwendung eines solchen Musters zurückgeben?
BlackVegetable
8
@BlackVegetable IEnumeratorist schreibgeschützt, IEnumerablekann jedoch normalerweise auf seinen veränderlichen Typ zurückgesetzt werden (z. B. ((List<string>)foo.ReadOnlyValues).Add("Hahaha! So much for read only sucker!")). Seien Sie also vorsichtig, wenn Sie davon ausgehen, dass dies IEnumerableSie davor schützt.
Jonathan Dickinson
@ JonathanDickinson Eh ... eigentlich macht das alle Vorteile der Verwendung von IEnumerable zunichte . Das typische Muster besteht darin, eine neue IEnumerable mit den neuen Werten zurückzugeben. Auf diese Weise kann der Client-Code entscheiden, wann jedes Element der Aufzählung ausgewertet werden soll, anstatt alle gleichzeitig zu erzwingen.
Jpaugh
18
public IEnumerable<string> Appender(IEnumerable<string> strings)
{
  List<string> myList = new List<string>();
  foreach(string str in strings)
  {
      myList.Add(str + "roxxors");
  }
  return myList;
}

oder

public IEnumerable<string> Appender(IEnumerable<string> strings)
{
  foreach(string str in strings)
  {
      yield return str + "roxxors";
  }
}

unter Verwendung des Ertragskonstrukts oder einfach

var newCollection = strings.Select(str => str + "roxxors"); //(*)

oder

var newCollection = from str in strings select str + "roxxors"; //(**)

wo die beiden letzteren LINQ verwenden und (**)nur syntaktischer Zucker für sind (*).

Rune
quelle
Interessantes Muster mit dem LINQ. Das muss ich recherchieren. Vielen Dank für Ihre (schnelle) Hilfe!
BlackVegetable
Ich erinnere mich, dass Sie versuchen sollten, keine Liste in einer als IEnumerable deklarierten Methode zurückzugeben. Der Grund dafür ist, dass die Leute möglicherweise "schummeln" und bemerken, dass es sich tatsächlich um eine Liste handelt, und anfangen, Listensachen damit zu machen. List (und in der Tat alle IEnumerables) enthält eine AsEnumerable () -Methode, die meiner Meinung nach aufgerufen und zurückgegeben werden sollte. Ansonsten Top-Antwort für die Ausbeute und insbesondere Linq-Antworten. :)
Chris
Wenn Sie vorhaben, in C # erhebliche Entwicklungsarbeiten durchzuführen, sollten Sie auf jeden Fall etwas Zeit in LINQ investieren - dies spart Ihnen viel Zeit und sorgt dafür, dass Sie sich bei Ihrem Code besser fühlen :-)
Rune
@chris: Das Aufrufen von .AsEnumerable () macht nicht viel: stackoverflow.com/questions/17968469/… :-)
Rune
1
@rune: Das ist ein bisschen wie eine Explosion aus der Vergangenheit. :) Sie haben Recht, dass die AsEnumerable-Methode in IEnumerables nichts Nützliches bewirkt. Meine Argumentation ist immer noch vernünftig (ish), erfordert jedoch eine andere Methode, um nicht mehr als Liste zurückgesetzt werden zu können (z .Selectg(x=>x). B. ).
Chris
8

Wenn ich dich richtig verstehe, dann ist in c # die yield returnCompiler-Magie alles, was du brauchst, denke ich.

z.B

IEnumerable<string> myMethod(IEnumerable<string> sequence)
{
    foreach(string item in sequence)
    {
         yield return item + "roxxors";
    }
}
Ben Robinson
quelle
In diesem Fall gibt Yield bei jeder Ausführung einen einzelnen (geänderten) String aus der Liste zurück. Oder wird es eine Art IEnumerable Object zurückgeben?
BlackVegetable
@ BlackVegetable Ja, es wird ein IEnumerableObjekt zurückgegeben, das bei jedem MoveNextAufruf eine Zeichenfolge erstellt (entweder direkt oder indirekt mit foreach)
R. Martinho Fernandes
Ja, es ist syntaktischer Zucker. Er weist den Compiler an, dies in einen Iteratorblock umzuwandeln. Siehe hier msdn.microsoft.com/en-us/library/9k7k7cf0.aspx
Ben Robinson
Dies ist die richtige Methode und sollte steigen. Die akzeptierte Antwort muss die gesamte Liste erstellen, bevor sie zurückgegeben wird, und nicht dem IEnumerable-Design folgen.
Thibault D.
5

Ich würde so etwas tun wie:

private IEnumerable<string> DoWork(IEnumerable<string> data)
{
    List<string> newData = new List<string>();
    foreach(string item in data)
    {
        newData.Add(item + "roxxors");
    }
    return newData;
}

Einfaches Zeug :)

Mike
quelle
Dies war mein erster Gedanke. Bei anderen Methoden, mit denen ich interagiere, muss ich jedoch einen IEnumerable-Typ oder ähnliches zurückgeben. Vielen Dank für die Antwort!
BlackVegetable
@BlackVegetable: List<T>ist ein IEnumerable<T>. Aber ja, es gibt bessere Möglichkeiten, dies zu tun (mit yield).
R. Martinho Fernandes
2
Ich erinnere mich, dass Sie versuchen sollten, keine Liste in einer als IEnumerable deklarierten Methode zurückzugeben. Der Grund dafür ist, dass die Leute möglicherweise "schummeln" und bemerken, dass es sich tatsächlich um eine Liste handelt, und anfangen, Listensachen damit zu machen. List (und in der Tat alle IEnumerables) enthält eine AsEnumerable () -Methode, die meiner Meinung nach aufgerufen und zurückgegeben werden sollte.
Chris
4

Sie können auch die Auswahlmethode von LINQ verwenden:

var source = new[] { "Line 1", "Line 2" };

var result = source.Select(s => s + " roxxors");

Lesen Sie hier mehr Enumerable.Select-Methode

Slava Kovalchuk
quelle