Was sind die Hauptunterschiede zwischen IEnumerable
Count()
und Length
?
Wenn Sie Count für IEnumerable<T>
Aufrufen, nehmen wir an, Sie beziehen sich auf die Erweiterungsmethode Count
für System.Linq.Enumerable
. Length
ist keine Methode für IEnumerable<T>
, sondern eine Eigenschaft für Array-Typen in .NET wie int[]
.
Der Unterschied liegt in der Leistung. Die Length
-Eigenschaft ist garantiert eine O(1)- Operation. Die Komplexität der Count
-Erweiterungsmethode hängt vom Laufzeittyp des Objekts ab Versuchen Sie, mehrere Typen zu konvertieren, die die O(1) Längen-Suche wie ICollection<T>
über eine Count
-Eigenschaft unterstützen. Wenn keine verfügbar sind, werden alle Elemente aufgelistet und zähle sie, die eine Komplexität von O (N) haben.
Beispielsweise
int[] list = CreateSomeList();
Console.WriteLine(list.Length); // O(1)
IEnumerable<int> e1 = list;
Console.WriteLine(e1.Count()); // O(1)
IEnumerable<int> e2 = list.Where(x => x <> 42);
Console.WriteLine(e2.Count()); // O(N)
Der Wert e2
Wird als C # -Iterator implementiert, der die Zählung von O(1) nicht unterstützt, und daher muss die Methode Count
die gesamte Sammlung auflisten, um zu bestimmen, wie lang ist es.
Kleiner Zusatz zu Jon Skeet s Kommentar.
Hier ist der Quellcode der Count()
-Erweiterungsmethode:
.NET 3:
public static int Count<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
ICollection<TSource> is2 = source as ICollection<TSource>;
if (is2 != null)
{
return is2.Count;
}
int num = 0;
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
num++;
}
}
return num;
}
.NET 4:
public static int Count<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
ICollection<TSource> is2 = source as ICollection<TSource>;
if (is2 != null)
{
return is2.Count;
}
ICollection is3 = source as ICollection;
if (is3 != null)
{
return is3.Count;
}
int num = 0;
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
num++;
}
}
return num;
}
Länge ist eine feste Eigenschaft, z.B. eines eindimensionalen Arrays oder Strings. Es ist also keine Zähloperation erforderlich (bei mehrdimensionalen Arrays wird die Größe aller Dimensionen multipliziert). O(1) hier bedeutet, dass die Abrufzeit immer gleich ist, egal wie viele Elemente es gibt. Eine lineare Suche wäre (im Gegensatz dazu) O (n).
Die Count-Eigenschaft für ICollections (z. B. List und List <T>) kann geändert werden. Sie muss daher entweder bei Hinzufügen/Entfernen-Vorgängen aktualisiert werden oder wenn Count angefordert wird, nachdem die Collection geändert wurde. Hängt von der Implementierung des Objekts ab.
Die Count () - Methode von LINQ iteriert grundsätzlich JEDES MAL, wenn sie aufgerufen wird (außer wenn das Objekt ein ICollection-Typ ist, wird die ICollection.Count-Eigenschaft angefordert).
Beachten Sie, dass IEnumerables häufig nicht bereits definierte Objektsammlungen sind (wie Listen, Arrays, Hashtables usw.), sondern Verknüpfungen zu Hintergrundoperationen, die Ergebnisse generieren, wenn sie angefordert werden (als verzögerte Ausführung bezeichnet).
In der Regel haben Sie eine SQL-ähnliche LINQ-Anweisung wie diese (die typische Anwendung der verzögerten Ausführung):
IEnumerable<Person> deptLeaders =
from p in persons
join d in departments
on p.ID equals d.LeaderID
orderby p.LastName, p.FirstName
select p;
Dann gibt es folgenden Code:
if (deptLeaders.Count() > 0)
{
ReportNumberOfDeptLeaders(deptLeaders.Count());
if (deptLeaders.Count() > 20)
WarnTooManyDepartmentLeaders(deptLeaders.Count());
}
Wenn also eine Warnung für zu viele Abteilungsleiter ausgegeben wird, durchsucht .NET die Personen VIER MAL, vergleicht sie mit den Abteilungsleitern, sortiert sie nach Namen und zählt dann die Ergebnisobjekte.
Und das nur, wenn Personen und Abteilungen Vorgabewertesammlungen sind, nicht Abfragen selbst.