45 Stimmen

LINQ für ein Objekt verwenden, das nur über GetEnumerator() verfügt

Können Sie LINQ in einem Objekt verwenden, das nur Add(), Remove(), Count(), Item() und GetEnumerator() von System.Collections.IEnumerator bereitstellt?

56voto

Thomas Levesque Punkte 277723

Die bestehenden Linq-Erweiterungsmethoden arbeiten mit Objekten, die die IEnumerable<T> . Ich nehme an, Ihr Objekt implementiert die nicht-generische IEnumerable Schnittstelle. In diesem Fall können Sie die Cast<T> Erweiterungsmethode, um eine generische IEnumerable<T> Umschlag. Wenn die Elemente zum Beispiel vom Typ int :

var wrapper = myObject.Cast<int>();

Sie können jetzt Linq auf dem Wrapper verwenden

7voto

Jon Skeet Punkte 1325502

Nicht direkt. Sie könnten relativ einfach eine SingleShotEnumerable<T> Allerdings:

 public sealed class SingleShotEnumerable<T> : IEnumerable<T>
 {
     private IEnumerator<T> enumerator;

     public SingleShotEnumerable(IEnumerator<T> enumerator)
     {
         if (enumerator == null)
         {
             throw new ArgumentNullException("enumerator");
         }
         this.enumerator = enumerator;
     }

     public IEnumerator<T> GetEnumerator()
     {
         if (enumerator == null)
         {
             throw new InvalidOperationException
                 ("GetEnumerator can only be called once");
         }
         var ret = enumerator;
         enumerator = null;
         return ret;
     }

     IEnumerator IEnumerable.GetEnumerator()
     {
         return GetEnumerator();
     }
 }

Dies setzt voraus, dass Sie eigentlich haben eine IEnumerator<T> . Wenn Sie nur eine IEnumerator könnten Sie etwas Ähnliches schreiben, nur dass Sie IEnumerable verwenden, dann Cast o OfType um zu einem IEnumerable<T> .

(Hinweis: Dies ist nicht thread-sicher. Sie könnten es mit Sperren so machen, wenn Sie wirklich wollten).

Das könnten Sie dann tun:

 var filtered = from person in new SingleShotEnumerable<Person>(personEnumerator)
                where person.Age > 18
                select person.Name;

... aber Sie können die Abfrage nicht zweimal verwenden.

Wie sind Sie in die merkwürdige Situation gekommen, dass Sie nur eine IEnumerator<T> überhaupt? Das ist ziemlich selten. Versuchen Sie, das zu umgehen, um zu vermeiden, dass Sie etwas wie das oben beschriebene tun müssen, das ziemlich anfällig ist.

(Eine Alternative wäre die "Entleerung" der IEnumerator<T> zu einer List<T> oder etwas Ähnliches, aber das hat Probleme mit großen oder potenziell unendlich Sequenzen).

0 Stimmen

Einige System.Windows.Forms Typen, wie zum Beispiel ListViewItemCollection scheinen nicht zu implementieren IEnumerable<T> .

0 Stimmen

@ChrisAkridge: In der Tat, sie implementieren nur die nicht-generischen IEnumerable . Aber Sie können jederzeit anrufen Cast<> zur Erstellung einer IEnumerable<T> von ihm.

0voto

Philippe Leybaert Punkte 161931

Nein, das können Sie nicht. Alle LINQ-Methoden sind Erweiterungsmethoden für die IEnumerable<T> Schnittstelle.

Sie müssen also Folgendes implementieren IEnumerable<T> um LINQ mit Ihren eigenen Sammlungen zu verwenden.

0voto

kofifus Punkte 14007

Verwenden Sie eine Hilfsklasse:

namespace System.Linq {
  public sealed class EnumerableWrapper<T> : IEnumerable<T> {
    readonly IEnumerator<T> Enumerator;
    public EnumerableWrapper(IEnumerator<T> enumerator) => Enumerator = enumerator;
    public IEnumerator<T> GetEnumerator() => Enumerator;
    Collections.IEnumerator Collections.IEnumerable.GetEnumerator() => GetEnumerator();
  }
}

Dann verwenden Sie Linq auf eine neue Instanz von EnumerableWrapper

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X