In C # 3.0 gefällt mir dieser Stil:
// Write the numbers 1 thru 7
foreach (int index in Enumerable.Range( 1, 7 ))
{
Console.WriteLine(index);
}
über die traditionelle for
Schleife:
// Write the numbers 1 thru 7
for (int index = 1; index <= 7; index++)
{
Console.WriteLine( index );
}
Unter der Annahme, dass 'n' klein ist, sodass Leistung kein Problem darstellt, hat jemand Einwände gegen den neuen Stil gegenüber dem traditionellen Stil?
Antworten:
Ich finde das "Minimum-to-Maximum" -Format des letzteren viel klarer als
Range
den "Minimum-Count" -Stil für diesen Zweck. Ich denke auch nicht, dass es wirklich eine gute Praxis ist, eine solche Änderung von der Norm vorzunehmen, die nicht schneller, nicht kürzer, nicht vertrauter und nicht offensichtlich klarer ist.Das heißt, ich bin nicht gegen die Idee im Allgemeinen. Wenn Sie mit einer Syntax auf mich zukommen würden, die ungefähr so
foreach (int x from 1 to 8)
aussieht, würde ich wahrscheinlich zustimmen, dass dies eine Verbesserung gegenüber einerfor
Schleife wäre. AllerdingsEnumerable.Range
ist ziemlich klobig.quelle
For i = 1 To 8
foreach(var x in (1 to 8))
von Scala? Es ist ein Aufzählungstyp.Das ist nur zum Spaß. (Ich würde nur das Standardschleifenformat
for (int i = 1; i <= 10; i++)
selbst verwenden.)foreach (int i in 1.To(10)) { Console.WriteLine(i); // 1,2,3,4,5,6,7,8,9,10 } // ... public static IEnumerable<int> To(this int from, int to) { if (from < to) { while (from <= to) { yield return from++; } } else { while (from >= to) { yield return from--; } } }
Sie können auch eine
Step
Erweiterungsmethode hinzufügen :foreach (int i in 5.To(-9).Step(2)) { Console.WriteLine(i); // 5,3,1,-1,-3,-5,-7,-9 } // ... public static IEnumerable<T> Step<T>(this IEnumerable<T> source, int step) { if (step == 0) { throw new ArgumentOutOfRangeException("step", "Param cannot be zero."); } return source.Where((x, i) => (i % step) == 0); }
quelle
Step
Methode funktioniert korrekt:i
ist der Index, nicht der Wert. Sie haben Recht, dass die Leistung nicht großartig ist, aber dies ist eine Proof-of-Concept-Demo, kein Produktionscode.In C # 6.0 mit der Verwendung von
using static System.Linq.Enumerable;
Sie können es vereinfachen
foreach (var index in Range(1, 7)) { Console.WriteLine(index); }
quelle
using static
. Es sollte erwähnt werden, dass lesbarer zu schreiben:Enumerable.Range(1, 7).ToList().ForEach(Console.WriteLine);
Sie können dies in C # tatsächlich tun (durch die Bereitstellung
To
undDo
als Erweiterungsmethoden aufint
undIEnumerable<T>
jeweils):1.To(7).Do(Console.WriteLine);
SmallTalk für immer!
quelle
Do
Erweiterungsmethode nicht. Es ist klarer, wenn dies in eineforeach
Schleife geht , wie andere vorgeschlagen haben.Ich mag die Idee irgendwie. Es ist Python sehr ähnlich. Hier ist meine Version in ein paar Zeilen:
static class Extensions { public static IEnumerable<int> To(this int from, int to, int step = 1) { if (step == 0) throw new ArgumentOutOfRangeException("step", "step cannot be zero"); // stop if next `step` reaches or oversteps `to`, in either +/- direction while (!(step > 0 ^ from < to) && from != to) { yield return from; from += step; } } }
Es funktioniert wie bei Python:
0.To(4)
→[ 0, 1, 2, 3 ]
4.To(0)
→[ 4, 3, 2, 1 ]
4.To(4)
→[ ]
7.To(-3, -3)
→[ 7, 4, 1, -2 ]
quelle
!(step > 0 ^ from < to)
könnte umgeschrieben werdenstep > 0 == from < to
. Oder noch knapper: Verwenden Sie eine Do-While-Schleife mit Endbedingungfrom += step != to
Es scheint eine ziemlich langwierige Herangehensweise an ein Problem zu sein, das bereits gelöst ist. Es gibt eine ganze Zustandsmaschine hinter der
Enumerable.Range
, die nicht wirklich benötigt wird.Das traditionelle Format ist grundlegend für die Entwicklung und allen bekannt. Ich sehe keinen wirklichen Vorteil für Ihren neuen Stil.
quelle
Ich denke, der foreach + Enumerable.Range ist weniger fehleranfällig (Sie haben weniger Kontrolle und weniger Möglichkeiten, es falsch zu machen, wie das Verringern des Index innerhalb des Körpers, damit die Schleife niemals endet usw.)
Das Lesbarkeitsproblem betrifft die Semantik der Bereichsfunktion, die von einer Sprache in eine andere wechseln kann (z. B. wenn nur ein Parameter angegeben wird, beginnt er bei 0 oder 1 oder ist das Ende eingeschlossen oder ausgeschlossen oder ist der zweite Parameter eine Zählung statt ein Ende Wert).
In Bezug auf die Leistung denke ich, dass der Compiler intelligent genug sein sollte, um beide Schleifen zu optimieren, damit sie auch bei großen Bereichen mit einer ähnlichen Geschwindigkeit ausgeführt werden (ich nehme an, dass Range keine Sammlung erstellt, sondern natürlich einen Iterator).
quelle
Ich denke, Range ist nützlich für die Arbeit mit einigen Inline-Bereichen:
var squares = Enumerable.Range(1, 7).Select(i => i * i);
Sie können jeweils vorbei. Erfordert die Konvertierung in eine Liste, hält die Dinge jedoch kompakt, wenn Sie dies wünschen.
Enumerable.Range(1, 7).ToList().ForEach(i => Console.WriteLine(i));
Aber anders als für so etwas würde ich traditionelle for-Schleife verwenden.
quelle
var paramList = String.Join(",", Enumerable.Range(0, values.Length).Select(i => "@myparam" + i));
Enumerable.Range(1, 7).ToList().ForEach(Console.WriteLine);
BESSERIch möchte die Syntax einiger anderer Sprachen wie Python, Haskell usw. haben.
// Write the numbers 1 thru 7 foreach (int index in [1..7]) { Console.WriteLine(index); }
Zum Glück haben wir jetzt F # :)
Was C # betrifft, muss ich mich an die
Enumerable.Range
Methode halten.quelle
@ Luke: Ich habe Ihre
To()
Erweiterungsmethode neu implementiert und dieEnumerable.Range()
Methode verwendet, um dies zu tun. Auf diese Weise wird es etwas kürzer und verwendet so viel Infrastruktur wie möglich von .NET:public static IEnumerable<int> To(this int from, int to) { return from < to ? Enumerable.Range(from, to - from + 1) : Enumerable.Range(to, from - to + 1).Reverse(); }
quelle
So verwenden Sie heute eine neue Syntax
Aufgrund dieser Frage habe ich einige Dinge ausprobiert, um eine nette Syntax zu finden, ohne auf erstklassige Sprachunterstützung zu warten. Folgendes habe ich:
using static Enumerizer; // prints: 0 1 2 3 4 5 6 7 8 9 foreach (int i in 0 <= i < 10) Console.Write(i + " ");
Nicht der Unterschied zwischen
<=
und<
.Ich habe auch ein Proof-of-Concept-Repository auf GitHub mit noch mehr Funktionen erstellt (umgekehrte Iteration, benutzerdefinierte Schrittgröße).
Eine minimale und sehr begrenzte Implementierung der obigen Schleife würde ungefähr so aussehen:
public readonly struct Enumerizer { public static readonly Enumerizer i = default; public Enumerizer(int start) => Start = start; public readonly int Start; public static Enumerizer operator <(int start, Enumerizer _) => new Enumerizer(start); public static Enumerizer operator >(int _, Enumerizer __) => throw new NotImplementedException(); public static IEnumerable<int> operator <=(Enumerizer start, int end) { for (int i = start.Start; i < end; i++) yield return i; } public static IEnumerable<int> operator >=(Enumerizer _, int __) => throw new NotImplementedException(); }
quelle
Ich bin sicher, dass jeder seine persönlichen Vorlieben hat (viele würden das spätere bevorzugen, nur weil es fast allen Programmiersprachen bekannt ist), aber ich bin wie Sie und fange an, das foreach immer mehr zu mögen, besonders jetzt, wo Sie einen Bereich definieren können .
quelle
Meiner Meinung nach ist der
Enumerable.Range()
Weg aussagekräftiger. Neu und den Menschen unbekannt? Bestimmt. Ich denke jedoch, dass dieser deklarative Ansatz die gleichen Vorteile bietet wie die meisten anderen LINQ-bezogenen Sprachfunktionen.quelle
Ich stelle mir vor, dass es Szenarien geben könnte, in
Enumerable.Range(index, count)
denen der Umgang mit Ausdrücken für die Parameter klarer ist, insbesondere wenn einige der Werte in diesem Ausdruck innerhalb der Schleife geändert werden. Im Fall desfor
Ausdrucks würde basierend auf dem Zustand nach der aktuellen IterationEnumerable.Range()
ausgewertet , während im Voraus ausgewertet wird.Abgesehen davon
for
würde ich zustimmen, dass das Festhalten an normalerweise besser ist (vertrauter / lesbarer für mehr Leute ... lesbar ist ein sehr wichtiger Wert in Code, der beibehalten werden muss).quelle
Ich bin damit einverstanden, dass in vielen (oder sogar den meisten Fällen)
foreach
viel besser lesbar ist als eine Standardschleife,for
wenn einfach über eine Sammlung iteriert wird. Ihre Wahl der VerwendungEnumerable.Range(index, count)
ist jedoch kein gutes Beispiel für den Wert vonforeach
over for.Für einen einfachen Bereich ab
1, Enumerable.Range(index, count)
sieht gut lesbar aus. Wenn der Bereich jedoch mit einem anderen Index beginnt, ist er weniger lesbar, da Sie eine ordnungsgemäße Ausführung durchführen müssenindex + count - 1
, um das letzte Element zu bestimmen. Zum Beispiel…// Write the numbers 2 thru 8 foreach (var index in Enumerable.Range( 2, 7 )) { Console.WriteLine(index); }
In diesem Fall bevorzuge ich das zweite Beispiel.
// Write the numbers 2 thru 8 for (int index = 2; index <= 8; index++) { Console.WriteLine(index); }
quelle
Genau genommen missbrauchen Sie die Aufzählung.
Enumerator bietet die Möglichkeit, einzeln auf alle Objekte in einem Container zuzugreifen, garantiert jedoch nicht die Reihenfolge.
Es ist in Ordnung, die Aufzählung zu verwenden, um die größte Zahl in einem Array zu finden. Wenn Sie damit beispielsweise das erste Element ungleich Null finden, verlassen Sie sich auf die Implementierungsdetails, die Sie nicht kennen sollten. In Ihrem Beispiel scheint Ihnen die Reihenfolge wichtig zu sein.
Edit : Ich liege falsch. Wie Luke betonte (siehe Kommentare), ist es sicher, sich bei der Aufzählung eines Arrays in C # auf die Reihenfolge zu verlassen. Dies unterscheidet sich beispielsweise von der Verwendung von "for in" zum Auflisten eines Arrays in Javascript.
quelle
Ich mag den
foreach
+Enumerable.Range
Ansatz und benutze ihn manchmal.// does anyone object to the new style over the traditional style? foreach (var index in Enumerable.Range(1, 7))
Ich lehne den
var
Missbrauch in Ihrem Vorschlag ab. Ich weiß es zu schätzenvar
, aber verdammt, schreibe einfachint
in diesem Fall! ;-);quelle