Frage

Dies ist in C #, ich eine Klasse, die ich von einigen anderen DLL verwenden. Dabei spielt es keine IEnumerable implementieren, sondern hat zwei Methoden, die einen IEnumerator Pass zurück. Gibt es eine Möglichkeit ich eine foreach-Schleife auf diese nutzen kann. Die Klasse Ich benutze ist versiegelt.

War es hilfreich?

Lösung

foreach tut nicht erfordert IEnumerable, entgegen der landläufigen Meinung. Alles was es braucht ist eine Methode GetEnumerator, die jedes Objekt zurückgibt, das die Methode MoveNext und die get-Eigenschaft Current mit den entsprechenden Unterschriften hat.

/ EDIT: In Ihrem Fall jedoch, bist du kein Glück. Sie können Ihr Objekt trivialer wickeln, jedoch machen es enumerable:

class EnumerableWrapper {
    private readonly TheObjectType obj;

    public EnumerableWrapper(TheObjectType obj) {
        this.obj = obj;
    }

    public IEnumerator<YourType> GetEnumerator() {
        return obj.TheMethodReturningTheIEnumerator();
    }
}

// Called like this:

foreach (var xyz in new EnumerableWrapper(yourObj))
    …;

/ EDIT: Die folgende Methode, die von mehreren Personen vorgeschlagen, tut nicht Arbeit, wenn die Methode einen IEnumerator:

foreach (var yz in yourObj.MethodA())
    …;

Andere Tipps

Re: Wenn foreach keinen expliziten Schnittstellenvertrag verlangt, es findet GetEnumerator Reflexion mit?

(Ich kann nicht sagen, da ich nicht über eine ausreichend hohe Reputation.)

Wenn Sie impliziert Laufzeit Reflexion dann nicht. Es macht alles compiletime, andere weniger bekannte Tatsache ist, dass es auch zu überprüfen, ob das zurückgegebene Objekt, dass könnte Implementieren Sie IEnumerator Einweg ist.

Um dies zu sehen in Aktion betrachtet diese (runnable) Schnipsel.


using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication3
{
    class FakeIterator
    {
        int _count;

        public FakeIterator(int count)
        {
            _count = count;
        }
        public string Current { get { return "Hello World!"; } }
        public bool MoveNext()
        {
            if(_count-- > 0)
                return true;
            return false;
        }
    }

    class FakeCollection
    {
        public FakeIterator GetEnumerator() { return new FakeIterator(3); }
    }

    class Program
    {
        static void Main(string[] args)
        {
            foreach (string value in new FakeCollection())
                Console.WriteLine(value);
        }
    }
}

Nach MSDN :

foreach (type identifier in expression) statement

Dabei gilt Ausdruck:

  

Objektsammlung oder Array-Ausdruck.   Der Typ des Sammelelement   muss dem Identifikator konvertierbar sein   Art. Verwenden Sie keinen Ausdruck verwenden, die   wertet auf null. Wertet auf einen Typ   daß implementiert IEnumerable oder einen Typ   dass deklariert eine Methode GetEnumerator.   Im letzteren Fall GetEnumerator   sollte entweder einen Typ zurückgeben,   IEnumerator implementiert oder erklärt, alle   die Methoden definiert in IEnumerator.

Kurze Antwort:

Sie müssen eine Klasse mit einer Methode namens GetEnumerator , die die IEnumerator zurück Sie bereits haben. Sie erreicht dies mit einem einfachen Wrapper:

class ForeachWrapper
{
  private IEnumerator _enumerator;

  public ForeachWrapper(Func<IEnumerator> enumerator)
  {
    _enumerator = enumerator;
  }

  public IEnumerator GetEnumerator()
  {
    return _enumerator();
  }
}

Verbrauch:

foreach (var element in new ForeachWrapper(x => myClass.MyEnumerator()))
{
  ...
}

Aus dem C # Language Specification :

  

Die Kompilierung-Verarbeitung eines   foreach-Anweisung zuerst bestimmt die   Kollektionstyp, Enumeratortyp und   Elementtyp des Ausdrucks. Diese   Bestimmung geht wie folgt vor:

     
      
  • Wenn der Typ X des Ausdrucks ein Array-Typ, dann gibt es eine implizite   Referenz Umwandlung von X zu dem   System.Collections.IEnumerable   Schnittstelle (seit System.Array   implementiert diese Schnittstelle). Das   Kollektionstyp ist die   System.Collections.IEnumerable   Schnittstelle, der Enumerator Typ ist die   System.Collections.IEnumerator   Schnittstelle und der Elementtyp ist das   Elementtyp des Array-Typ X.

  •   
  • Ansonsten bestimmen, ob der Typ X ein geeignetes hat   GetEnumerator-Methode:

         
        
    • Führen Sie Membersuche nach Typ X mit der Kennung GetEnumerator und keine   Geben Sie Argumente. Wenn die Membersuche   produziert nicht ein Spiel, oder es   erzeugt eine Zweideutigkeit, oder erzeugt ein   Spiel, das kein Verfahren Gruppe ist,   Check für eine zählbare Schnittstelle   nachstehend beschrieben. Es wird empfohlen   dass eine Warnung, wenn das Mitglied ausgestellt   Lookup produziert nichts außer einem   Verfahren Gruppe oder kein Spiel.

    •   
    • Überladungsauflösung Führen Sie die resultierende Methodengruppe mit und ein   leere Argumentliste. wenn Überlastung   Auflösung führt zu keiner anwendbar   Verfahren, führt zu einer Zweideutigkeit, oder   Ergebnisse in einer einzigen besten Methode, aber   daß Verfahren ist entweder statisch oder nicht,   Öffentlichkeit, überprüfen Sie für eine zählbare   eine Schnittstelle, wie unten beschrieben. Es ist   empfohlen, dass eine Warnung ausgegeben werden,   wenn die Überladungsauflösung erzeugt   alles außer einer eindeutigen öffentlichen   Instanzmethode oder nicht anwendbar   Methoden.

    •   
    • Wenn der Rückgabetyp E der GetEnumerator-Methode nicht eine Klasse ist,   struct oder Schnittstellentyp, ist ein Fehler,   erzeugt und es werden keine weiteren Schritte   genommen.

    •   
    • Membersuche auf E mit der Kennung Strom durchgeführt und keine   Geben Sie Argumente. Wenn die Membersuche   keine Übereinstimmung ergibt, ist das Ergebnis ein   Fehler oder das Ergebnis ist alles   außer einer öffentlichen Instanz Eigenschaft, dass   ermöglicht das Lesen, wird ein Fehler erzeugt   und keine weiteren Schritte unternommen werden.

    •   
    • Membersuche auf E mit der Kennung durchgeführt und kein Movenext   Geben Sie Argumente. Wenn die Membersuche   keine Übereinstimmung ergibt, ist das Ergebnis ein   Fehler oder das Ergebnis ist alles   mit Ausnahme eines Verfahrens Gruppe, ein Fehler ist,   erzeugt und es werden keine weiteren Schritte   genommen.

    •   
    • Die Überladungsauflösung wird mit einem leeren auf der Methodengruppe durchgeführt   Argumentliste. Wenn die Überladungsauflösung   Ergebnisse in keine anwendbare Methoden,   führt zu einer Zweideutigkeit oder Ergebnisse in   eine einzelne beste Methode, aber das Verfahren   ist entweder statisch oder nicht öffentlich oder sein   Rückgabetyp ist nicht Bool, ein Fehler ist   erzeugt und es werden keine weiteren Schritte   genommen.

    •   
    • Der Kollektionstyp X ist, ist der Enumerator Typ E, und das Element   Typ ist der Typ des aktuellen   Eigenschaft.

    •   
  •   
  • Andernfalls überprüfen Sie für eine zählbare Schnittstelle:

         
        
    • Wenn es genau einen Typ T, so ist, dass es eine implizite ist   Umwandlung von X in die Schnittstelle   System.Collections.Generic.IEnumerable ,   dann ist die Sammlung Typ dieser   Schnittstelle, der Enumerator Typ ist die   Schnittstelle   System.Collections.Generic.IEnumerator ,   und der Elementtyp ist T.

    •   
    • Ansonsten, wenn es mehr als einen solchen Typ T ist, dann ist ein Fehler   erzeugt und es werden keine weiteren Schritte   genommen.

    •   
    • Ansonsten i, wenn ess eine implizite Umwandlung von X zu dem   System.Collections.IEnumerable   Schnittstelle, dann wird der Sammeltyp ist   Diese Schnittstelle ist die Enumeratortyp   die Schnittstelle   System.Collections.IEnumerator und   der Elementtyp ist Objekt.

    •   
    • Ansonsten wird ein Fehler erzeugt und keine weiteren Schritte unternommen werden.

    •   
  •   

Nicht streng. Solange die Klasse die erforderliche GetEnumerator hat, Movenext, Reset und aktuelle Mitglieder, wird es mit foreach arbeiten

Nein, tun Sie nicht und Sie haben nicht einmal eine GetEnumerator-Methode benötigen, z.

class Counter
{
    public IEnumerable<int> Count(int max)
    {
        int i = 0;
        while (i <= max)
        {
            yield return i;
            i++;
        }
        yield break;
    }
}

, die so genannt wird:

Counter cnt = new Counter();

foreach (var i in cnt.Count(6))
{
    Console.WriteLine(i);
}

Sie können immer es wickeln und als beiseite zu sein „foreachable“ Sie brauchen nur ein Verfahren haben „GetEnumerator“ mit der richtigen Signatur bezeichnet.


class EnumerableAdapter
{
  ExternalSillyClass _target;

  public EnumerableAdapter(ExternalSillyClass target)
  {
    _target = target;
  }

  public IEnumerable GetEnumerator(){ return _target.SomeMethodThatGivesAnEnumerator(); }

}

Bei Klasse X mit Methoden A und B, die beide IEnumerable, könnten Sie eine foreach auf die Klasse wie folgt verwendet werden:

foreach (object y in X.A())
{
    //...
}

// or

foreach (object y in X.B())
{
   //...
}

Vermutlich ist die Bedeutung für die enumerables von A und B zurückgegeben werden, gut definiert.

@ Brian: Nicht sicher, dass Sie Schleife versuchen, über den Wert der Rückkehr von Methodenaufruf oder die Klasse selbst, Wenn das, was Sie wollen die Klasse ist dann durch ein Array machen Sie mit foreach verwenden können.

Für eine Klasse verwendbar zu sein mit foeach alle es tun muss, ist eine öffentliche Methode, die zurückgibt und IEnumerator namens GetEnumerator (), das ist es:

Nehmen Sie die folgende Klasse, es ist nicht IEnumerable oder IEnumerator nicht implementiert:

public class Foo
{
    private int[] _someInts = { 1, 2, 3, 4, 5, 6 };
    public IEnumerator GetEnumerator()
    {
        foreach (var item in _someInts)
        {
            yield return item;
        }
    }
}

alternativ das GetEnumerator () -Methode geschrieben werden:

    public IEnumerator GetEnumerator()
    {
        return _someInts.GetEnumerator();
    }

Wenn in einer foreach verwendet (Beachten Sie, dass der nicht-Wrapper verwendet wird, nur eine Klasseninstanz):

    foreach (int item in new Foo())
    {
        Console.Write("{0,2}",item);
    }

druckt:

  

1 2 3 4 5 6

Der Typ benötigt nur eine Öffentlichkeit haben / nicht-statischen / nicht-generic / parameterlos Methode namens GetEnumerator, die etwas zurückgeben sollen, die ein öffentliches MoveNext Verfahren und eine öffentliche Current Eigenschaft hat. Als ich Herrn Eric Lippert irgendwo erinnern, dieses entworfen wurde, um vor generic Ära sowohl für Typsicherheit und Boxen im Zusammenhang von Leistungsproblemen bei Werttypen.

aufnehmen

Zum Beispiel das funktioniert:

class Test
{
    public SomethingEnumerator GetEnumerator()
    {

    }
}

class SomethingEnumerator
{
    public Something Current //could return anything
    {
        get { }
    }

    public bool MoveNext()
    {

    }
}

//now you can call
foreach (Something thing in new Test()) //type safe
{

}

Dies ist dann durch den Compiler zu übersetzt wird:

var enumerator = new Test().GetEnumerator();
try {
   Something element; //pre C# 5
   while (enumerator.MoveNext()) {
      Something element; //post C# 5
      element = (Something)enumerator.Current; //the cast!
      statement;
   }
}
finally {
   IDisposable disposable = enumerator as System.IDisposable;
   if (disposable != null) disposable.Dispose();
}

Von 8.8.4 Abschnitt der Spezifikation .


Etwas erwähnenswert ist der Enumerator Vorrang beteiligt - es geht so, wenn Sie eine public GetEnumerator Methode haben, dann, dass die Standardauswahl von foreach ist unabhängig davon, wer sie durchführt. Zum Beispiel:

class Test : IEnumerable<int>
{
    public SomethingEnumerator GetEnumerator()
    {
        //this one is called
    }

    IEnumerator<int> IEnumerable<int>.GetEnumerator()
    {

    }
}

( Wenn Sie (dh nur explizite Implementierung) kein öffentliches Umsetzung haben, dann Vorrang geht wie IEnumerator<T>> IEnumerator. )

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top