Wird dieser Code über alle Integer-Werte von 0 bis zur maximalen Range oder nur über die Integer-Werte iterieren, um die Take-While-, Where- und Select-Operatoren zu erfüllen? Kann jemand klären?
BEARBEITEN: Mein erster Versuch, um sicherzustellen, dass es wie erwartet funktioniert, war dumm. Ich widerrufe es:)
int.MaxValue + 5
läuft als negative Zahl über. Probieren Sie es selbst aus:
Das zweite Argument für Enumerable.Range
muss nicht-negativ sein - daher die Ausnahme.
Sie können jedoch in LINQ durchaus unendliche Sequenzen verwenden. Hier ist ein Beispiel für eine solche Sequenz:
%Vor%Das wird natürlich auch überlaufen, aber es wird weitergehen ...
Beachten Sie, dass einige LINQ-Operatoren (z. B. Reverse
) alle -Daten lesen müssen, bevor sie ihr erstes Ergebnis liefern können. Andere (wie Select
) können einfach die Ergebnisse streamen, während sie sie aus der Eingabe lesen. In meinen Edulinq-Blogposts finden Sie Einzelheiten zum Verhalten jedes Operators (in LINQ to Objects).
Der Weg, um diese Art von Fragen im Allgemeinen zu lösen, besteht darin, darüber nachzudenken, was in Schritten vor sich geht.
Linq wandelt den linq-Code in etwas um, das vom Abfrageanbieter ausgeführt wird. Dies könnte etwas wie die Erstellung von SQL-Code oder alle möglichen Dinge sein. Im Falle von linq-to-objects erzeugt es etwas äquivalenten .NET-Code. Wenn Sie darüber nachdenken, was dieser .NET-Code sein wird, können wir darüber nachdenken, was passieren wird. *
Mit Ihrem Code haben Sie:
%Vor%Enumerable.Range ist etwas komplizierter als:
%Vor%... aber das ist nah genug um der Sache willen.
Wählen ist nahe genug für:
%Vor%Wo ist nahe genug zu:
%Vor%TakeWhile ist nah genug an:
%Vor%Summe ist nah genug an:
%Vor%Dies vereinfacht einige Optimierungen und so weiter, ist aber nahe genug, um damit zu argumentieren. Nimmt man diese nacheinander, entspricht dein Code:
%Vor% Nun ist der obige Code in gewisser Weise einfacher und in mancher Hinsicht komplizierter. Der Sinn von LINQ liegt darin, dass es in vielerlei Hinsicht einfacher ist. Dennoch, um Ihre Frage zu beantworten: Wird dieser Code über alle Integer-Werte von 0 bis zum Maximalbereich oder nur über die Ganzzahlwerte iterieren, um die Take-While-, Where- und Select-Operatoren zu erfüllen? Wir können das Obige betrachten und sehen, dass diejenigen, die nicht das erfüllen, wo sie durchlaufen werden, feststellen, dass sie das Wo nicht erfüllen, aber keine Arbeit mehr mit ihnen gemacht wird, und sobald das TakeWhile erfüllt ist, alle weitere Arbeit ist gestoppt (die break
in meinem nicht-LINQ neu schreiben).
Natürlich ist es nur die TakeWhile()
in diesem Fall, was bedeutet, dass der Anruf in einer vernünftigen Zeitspanne zurückkehrt, aber wir müssen auch kurz über die anderen nachdenken, um sicherzustellen, dass sie nachgeben. Betrachten Sie die folgende Variante Ihres Codes:
Theoretisch wird dies genau die gleiche Antwort geben, aber es wird viel länger und viel mehr Speicher dazu benötigen (wahrscheinlich genug, um eine Nicht-Speicher-Ausnahme auszulösen). Der entsprechende Nicht-Linq-Code ist hier jedoch:
%Vor% Hier können wir sehen, wie sich das Hinzufügen von ToList()
zur Linq-Abfrage stark auf die Abfrage auswirkt, so dass jedes durch den Aufruf Range()
erzeugte Element behandelt werden muss. Methoden wie ToList()
und ToArray()
lösen die Verkettung auf, so dass Nicht-Linq-Äquivalente nicht mehr "ineinander" passen und keiner die Operationen derer stoppen kann, die davor stehen. ( Sum()
ist ein anderes Beispiel, aber da es nach Ihrem TakeWhile()
in Ihrem Beispiel ist, ist das kein Problem).
Eine andere Sache, die es dazu bringen würde, jede Iteration des Bereichs zu durchlaufen, wäre, wenn Sie While(x => false)
hätten, weil es den Test nie tatsächlich in TakeWhile
durchführen würde.
* Obwohl es weitere Optimierungen geben kann, insbesondere im Fall von SQL-Code und auch konzeptionell, z. Count()
entspricht:
Dass dies zu einem Aufruf der Eigenschaft Count
einer ICollection oder der Eigenschaft Length
eines Arrays wird, bedeutet, dass das obige O (n) durch ein O (1) ersetzt wird (für die meisten ICollection-Implementierungen) Anruf, der ein großer Gewinn für große Sequenzen ist.
Ihr erster Code wird nur iterieren, solange die Bedingung TakeWhile
erfüllt ist. Es wird nicht bis int.MaxValue
iterieren.
int.MaxValue + 5
führt zu einer negativen Ganzzahl. Enumerable.Range
löst eine ArgumentOutOfRangeException aus, wenn das zweite Argument negativ ist. Aus diesem Grund erhalten Sie die Ausnahme (bevor eine Iteration stattfindet).