2009-05-14 5 views
1

Also für einen Typ wie:Ist es möglich, mehrere Iteratoren für einen Typ in C# zu schreiben?

CoolCollection<T> 

könnten Sie haben:

foreach (T item in coolCollection) 
{ 
    ... 
} 

foreach (CoolNode node in coolCollection) 
{ 
    ... 
} 

Wenn dies nicht möglich ist, vielleicht wie foreach2, oder auf andere Weise zu durchlaufen. Oft möchte ich mehr als eine Art, auf einen Typ zu iterieren.

EDIT: Sorry, wenn es nicht klar war. Grundsätzlich ist CoolNode ein Knoten, der CoolCollection macht. CoolNode hat eine Eigenschaft namens value, um T zurückzugeben, aber ich brauche einen weiteren Iterator, um nur CoolNodes zurückzugeben.

EDIT2: Ich kann nicht coolCollection.Something zu iterieren, weil CoolNodes über eine Eigenschaft namens Next, wie eine LinkedList verbunden sind. Also muss ich 2 Iteratoren implementieren.

+0

Also, was ist die Frage? Sie können Ihre Sammlung nicht mehr als einmal durchlaufen? – GalacticCowboy

+0

Es ist nicht klar, was du meinst. Sind die verschiedenen Unterkollektionen von T und CoolNode oder nur verschiedene Ansichten derselben Sammlung? Für die erste, sollten Sie tun, wie J.13.L sagte und haben Methoden oder Eigenschaften, die eine entsprechende Enumerable für jeden zurückgeben. Für die zweite können Sie eine geeignete Besetzung oder Konvertierung verwenden. –

Antwort

3

einfach machen CoolCollection<T> ausdrücklich IEnumerable<CoolNode<T>> sowie IEnumerable<T> implementieren. (Ich schätze, es ist wirklich CoolNode<T>, aber wenn nicht, nehmen Sie einfach die zusätzlichen <T> überall.)

Dies wird Sie auf beide Arten iterieren lassen, obwohl Sie eine Besetzung benötigen.

Um dies zu tun, müssen Sie so etwas wie:

class CoolCollection<T> : ICollection<T>, IEnumerable<CoolNode<T>> 
{ 
    IEnumerator<CoolNode<T>> IEnumerable<CoolNode<T>>.GetEnumerator() 
    { 
     ///...Do work here... 
    } 

    IEnumerator<T> GetEnumerator() 
    { 
     ///...Do work here... 
    } 
} 

diese Verwendung wie so wären:

foreach (T item in coolCollection) 
{ 
    ... 
} 


foreach (CoolNode<T> node in (IEnumerable<CoolNode<T>>)coolCollection) 
{ 
    ... 
} 

Die andere Option, um eine Eigenschaft für den „Knoten“ zu entlarven wäre , so könnten Sie tun:

foreach(var nodes in coolCollection.Nodes) 
{ ... } 

Um dies zu implementieren, würden Sie die Dinge ein wenig ändern. Sie müssten eine private Klasse erstellen, die den Enumerator implementiert ... etwas wie:

class CoolCollection<T> : ICollection<T> 
{ 
    private List<CoolNode<T>> nodes; 

    IEnumerable<CoolNode<T>> Nodes 
    { 
     get 
     { 
      foreach(var node in this.nodes) { yield return node; } 
     } 
    } 
} 
+0

Danke Reed. Warum brauche ich eine Besetzung? Auch habe ich die Frage bearbeitet, um klarzustellen, ob es hilft. –

+0

Ich fügte ein Beispiel hinzu - Sie würden die Umwandlung benötigen, so dass der Compiler in diesem Fall coolCollection als IEnumerable anstelle von IEnumerable behandelt. Ich rate auch, es ist CoolNode , wenn die Knoten Teil der generischen Sammlung sind ... –

+0

Zumindest sollten Sie erwähnen, dass dies eine schlechte Praxis ist ... –

1

Nein, das geht nicht. Sie können Ihren Standard-Iterator nicht überladen.

Stellen Sie sich vor, Sie könnten Ihren Standard-Iterator überlasten.

Was würde das tun? foreach (object o in foo), würde es keinen logischen Weg geben, den richtigen Iterator zu wählen.

Sie können eine zweite Methode namens ForEach2 verwenden, die auf andere Weise durch Ihre Sammlung iteriert. Oder Sie könnten explizit eine Schnittstelle implementieren. Oder Sie könnten Linq-Komposition für diese Art von Sachen verwenden.

aus einer Klasse Design-Perspektive:

interface IBar { 
    IEnumerator<string> GetEnumerator(); 
} 

class Foo : IBar, IEnumerable<int> { 

    // Very bad, risky code. Enumerator implementations, should 
    // line up in your class design. 
    public IEnumerator<int> GetEnumerator() 
    { 
     yield return 1; 
     yield return 2; 
     yield return 3; 
     yield return 4; 
    } 

    IEnumerator<string> IBar.GetEnumerator() 
    { 
     yield return "hello"; 
    } 

    // must be IEnumerable if you want to support foreach 
    public IEnumerable<string> AnotherIterator 
    { 
     get { 
      yield return "hello2"; 
     } 
    } 


    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return this.GetEnumerator(); 
    } 

} 

LINQ-Erweiterungen für EachPair

struct Pair<T> { 
    public T First; 
    public T Second; 
} 

static class LinqExtension { 
    public static IEnumerable<Pair<T>> EachPair<T>(this IEnumerable<T> input) { 
     T first = default(T); 
     bool gotFirst = false; 
     foreach (var item in input) 
     { 
      if (!gotFirst) 
      { 
       first = item; 
       gotFirst = true; 
      } 
      else { 
       yield return new Pair<T>() { First = first, Second = item }; 
       gotFirst = false; 
      } 
     } 
    } 
} 

Prüfregeln:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var foo = new Foo(); 

     foreach (int number in foo) 
     { 
      Console.WriteLine(number); 
     } 

     // LINQ composition - a good trick where you want 
     // another way to iterate through the same data 
     foreach (var pair in foo.EachPair()) 
     { 
      Console.WriteLine("got pair {0} {1}", pair.First, pair.Second); 
     } 

     // This is a bad and dangerous practice. 
     // All default enumerators should be the same, otherwise 
     // people will get confused. 
     foreach (string str in (IBar)foo) 
     { 
      Console.WriteLine(str); 
     } 

     // Another possible way, which can be used to iterate through 
     // a portion of your collection eg. Dictionary.Keys 
     foreach (string str in foo.AnotherIterator) 
     { 
      Console.WriteLine(str); 
     } 
    } 
+0

Kann mir jemand sagen, warum das die schlechtere Antwort ist? –

+0

Im ersten Block: IEnumerable AnotherIterator sollte IEnumerator sein

+0

Auch: EachPair scheint nicht relevant zu sein? Was ist der Zweck hier? Es ist das Aufzählen und Konstruieren von Paaren (Element 0 & 1, dann 1 & 2, 2 & 3, ...) Scheint nicht mit den anderen Informationen übereinzustimmen .... –

2

Wenn ich die Frage richtig verstehe ...

Sie tun könnte es ähnlich die einige der anderen Sammlungsobjekte tun es:

zum Beispiel:

foreach (int key in IDictionary.Keys) 
{ 

} 

foreach (object value in IDictionary.Values) 
{ 

} 

Aber ich glaube nicht, dass es eine Möglichkeit, genau die Art und Weise zu tun, ist, dass Sie haben es geschrieben ...

0

Wenn CoolCollection IEnumerable implementiert, kann man schreiben:

foreach (var item in coolCollection) 
{ 
    ... 
} 

oder wenn T CoolNode

foreach (CoolNode node in coolCollection) 
{ 
    ... 
} 

ist Wenn Sie jedes Element Ihrer Art irgendwie umwandeln müssen, können Sie Linq Select-Operator verwenden:

foreach (CoolNode node in coolCollection.Select(item => ConvertToNode(item)) 
{ 
    ... 
} 
0

einen Blick auf die Schnipsel iterindex nehmen. Geben Sie in Ihrer Klasse iterindex ein und klicken Sie auf [TAB]. Es wird Ihnen helfen, ein "Named Iterator and Indexer" -Muster zu implementieren.

Das Ergebnis kann wie folgt verwendet von:

foreach (var e in myTree.DepthFirstView) // supports iteration 
{ 
    if (e == myTree.DepthFirstView[2]) // supports indexing 
    { 
     // ... 
    } 
} 

(ich diesen Schnipsel schrieb, aber ich vermute, dass es für einen guten Zweck wurde nie überhaupt..)