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:
IEnumerable<T>
erbt von*IEnumerable
, was bedeutet, dass jeder Typ, derIEnumerable<T>
muss generell umsetzen beideIEnumerable<T>.GetEnumerator
と (ausdrücklich)IEnumerable.GetEnumerator
. Im Grunde genommen handelt es sich um einen Standardcode.- Sie können
foreach
über jeden Typ, der eineGetEnumerator
Methode, solange diese Methode ein Objekt irgendeines Typs mit einerMoveNext
Methode und einCurrent
Eigentum. Wenn Ihr Typ also definiert eine Methode mit der Signaturpublic IEnumerator<T> GetEnumerator()
ist es legal, darüber zu enumerieren, indem manforeach
. - 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 manforeach
auf einenIEnumerable<T>
ist trivial, wenn man die automatische Iterator-Generierung verwendet, die C# über dieyield
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.