13 Stimmen

Warum IEnumerable(T) implementieren, wenn ich nur EINEN GetEnumerator definieren kann?

Update : Ich bedanke mich für alle Kommentare, die im Wesentlichen aus einhelliger Ablehnung bestanden haben. Obwohl jeder Einwand berechtigt war, glaube ich, dass der letzte Nagel im Sarg war Anis scharfsinnige Beobachtung dass letztlich sogar die eine winzige Der Vorteil, den diese Idee angeblich bot - die Beseitigung von Standardcode - wurde durch die Tatsache zunichte gemacht, dass die Idee selbst ihre eigene Code aus dem Baukasten.

Also ja, ich bin überzeugt: Es wäre eine schlechte Idee.

Und nur um meine Würde ein wenig zu retten: Ich habe es vielleicht um des Argumentes willen hochgespielt, aber ich war nie wirklich von dieser Idee überzeugt - ich war nur neugierig zu hören, was andere dazu zu sagen hatten. Ehrlich.


Bevor Sie diese Frage als absurd abtun, möchte ich Sie bitten, Folgendes zu bedenken:

  1. IEnumerable<T> erbt von* IEnumerable , was bedeutet, dass jeder Typ, der IEnumerable<T> muss generell umsetzen beide IEnumerable<T>.GetEnumerator (ausdrücklich) IEnumerable.GetEnumerator . Im Grunde genommen handelt es sich um einen Standardcode.
  2. Sie können foreach über jeden Typ, der eine GetEnumerator Methode, solange diese Methode ein Objekt irgendeines Typs mit einer MoveNext Methode und ein Current Eigentum. Wenn Ihr Typ also definiert eine Methode mit der Signatur public IEnumerator<T> GetEnumerator() ist es legal, darüber zu enumerieren, indem man foreach .
  3. Offensichtlich gibt es eine Menge Code, für den die IEnumerable<T> Schnittstelle - z. B. grundsätzlich alle LINQ-Erweiterungsmethoden. Glücklicherweise kann man von einem Typ, den man foreach auf einen IEnumerable<T> ist trivial, wenn man die automatische Iterator-Generierung verwendet, die C# über die yield Stichwort.

Als ich das alles zusammenfasste, hatte ich die verrückte Idee: Wie wäre es, wenn ich einfach meine eigene Schnittstelle definiere, die so aussieht?

public interface IForEachable<T>
{
    IEnumerator<T> GetEnumerator();
}

Dann Wenn ich einen Typ definiere, der aufzählbar sein soll, implementiere ich diese Schnittstelle anstelle von IEnumerable<T> Dadurch entfällt die Notwendigkeit, zwei GetEnumerator Methoden (eine explizit). Zum Beispiel:

class NaturalNumbers : IForEachable<int>
{
   public IEnumerator<int> GetEnumerator()
   {
       int i = 1;
       while (i < int.MaxValue)
       {
           yield return (i++);
       }
   }

   // Notice how I don't have to define a method like
   // IEnumerator IEnumerable.GetEnumerator().
}

Endlich um diesen Typ mit Code kompatibel zu machen, der tut erwarten die IEnumerable<T> Schnittstelle kann ich einfach eine Erweiterungsmethode definieren, um von jeder IForEachable<T> zu einer IEnumerable<T> etwa so:

public static class ForEachableExtensions
{
    public static IEnumerable<T> AsEnumerable<T>(this IForEachable<T> source)
    {
        foreach (T item in source)
        {
            yield return item;
        }
    }
}

Ich habe den Eindruck, dass ich auf diese Weise Typen entwerfen kann, die in jeder Hinsicht als Implementierungen von IEnumerable<T> aber ohne das lästige explizite IEnumerable.GetEnumerator Umsetzung in jedem einzelnen Fall.

Zum Beispiel:

var numbers = new NaturalNumbers();

// I can foreach myself...
foreach (int x in numbers)
{
    if (x > 100)
        break;

    if (x % 2 != 0)
        continue;

    Console.WriteLine(x);
}

// Or I can treat this object as an IEnumerable<T> implementation
// if I want to...
var evenNumbers = from x in numbers.AsEnumerable()
                  where x % 2 == 0
                  select x;

foreach (int x in evenNumbers.TakeWhile(i => i <= 100))
{
    Console.WriteLine(x);
}

Was haltet ihr von dieser Idee? Übersehe ich einen Grund, warum dies ein Fehler wäre?

Mir ist klar, dass dies wahrscheinlich nach einer übermäßig komplexen Lösung für ein Problem aussieht, das eigentlich gar nicht so groß ist (ich bezweifle, dass jemand wirklich sich so sehr darum kümmert, dass man explizit die IEnumerable Schnittstelle); aber es ist mir gerade in den Sinn gekommen und ich sehe keine offensichtlichen Probleme, die dieser Ansatz mit sich bringen würde.

Im Allgemeinen gilt: Wenn ich eine moderate Menge an Code schreiben kann einmal um mir die Mühe zu ersparen, einen kleinen Teil des Codes schreiben zu müssen viele Male für mich ist es das wert.

*Ist das die richtige Terminologie? Ich zögere immer, wenn ich sage, dass eine Schnittstelle von einer anderen "erbt", da dies die Beziehung zwischen ihnen nicht richtig zu erfassen scheint. Aber vielleicht ist es richtig.

0voto

Jimmy Hoffa Punkte 5803

Ich denke, jeder hat bereits die technischen Gründe erwähnt, dies nicht zu tun, also füge ich dies in den Mix hinzu: Wenn Sie von Ihrem Benutzer verlangen, AsEnumerable() auf Ihrer Sammlung aufzurufen, um aufzählbare Erweiterungen verwenden zu können, würde dies das Prinzip der geringsten Überraschung verletzen.

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