2010-03-26 6 views

Antwort

71

Mit dem Aufruf Count auf IEnumerable<T> nehme ich an, dass Sie sich auf die Erweiterungsmethode Count auf System.Linq.Enumerable beziehen. Length ist keine Methode auf IEnumerable<T> sondern eher eine Eigenschaft auf Array-Typen in .Net wie int[].

Der Unterschied ist die Leistung. Die Eigenschaft Length ist garantiert eine O (1) -Operation. Die Komplexität der Erweiterungsmethode Count hängt vom Laufzeittyp des Objekts ab. Es wird versucht, auf mehrere Typen zu transformieren, die eine 0 (1) -Längensuche wie ICollection<T> über eine Count-Eigenschaft unterstützen. Wenn keine verfügbar sind, wird es alle Elemente aufzählen und sie zählen, die eine Komplexität von O (N) hat.

Zum Beispiel

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 ist als C# Iterator implementiert, die nicht O (1) das Zählen und damit das Verfahren Count muss die gesamte Sammlung aufzuzählen unterstützt, um zu bestimmen, wie lang es ist.

+2

'Liste ' hat keine Eigenschaft Länge - es hat eine Count-Eigenschaft. Arrays haben jedoch eine 'Länge'. 'Count' wird in' ICollection' und 'ICollection ' angegeben (welches 'IList ' erweitert). –

+0

@ Jon, doh. Ich werde den Mangel an Schlaf hier verantwortlich machen. Wird update – JaredPar

+4

Und wenn Ihre 'IEnumerable ' ist unendlich lang, 'Count()' wird nie zurückkehren ... –

18

Kleiner Zusatz zu Jon Skeet 's Kommentar.

Es gibt Quellcode 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; 
} 
+6

Beachten Sie, dass in .NET 4 auch ein anderer Block für den nicht generischen 'ICollection'-Typ vorhanden ist. (Da dies auch eine "Count" -Eigenschaft hat.) –

+0

@ Jon Skeet: Danke – bniwredyc

+0

Weiß jemand was zu 'verwenden', um die' Fehler' Klasse zu bekommen, die diese Methode verwendet? Ich kann es nirgendwo auf MSDN finden, außer in der JScript-Dokumentation. –

1
  • Länge ist eine feste Eigenschaft, z.B. eines eindimensionalen Arrays oder einer Zeichenfolge. Es ist also nie eine Zähloperation erforderlich (multidimensionale Arrays haben eine Größe aller Dimensionen multipliziert). O (1) Operation bedeutet hier, dass die Suchzeit immer gleich ist, egal wie viele Elemente es gibt. Eine lineare Suche wäre (im Gegensatz dazu) O (n).

  • Der Count-Eigenschaft auf ICollections (List und List <T>, zum Beispiel) kann sich ändern, so hat es entweder auf Hinzufügen aktualisiert werden/Entfernen Operationen oder als Graf nach der Sammlung angefordert wird, hat sich geändert. 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, dann wird die ICollection.Count-Eigenschaft angefordert).

Beachten Sie, dass IEnumerables ist oft nicht bereits Objektsammlungen definiert (wie Listen, Arrays, Hash-Tabellen etc.), aber Link zu Hintergrundoperationen, die Ergebnisse erzeugen, wenn sie angefordert werden (verzögerte Ausführung bezeichnet).

Typischerweise haben Sie eine SQL wie LINQ-Anweisung wie folgt aus (die typische Anwendung von verzögerte 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 Code wie folgt:

if (deptLeaders.Count() > 0) 
{ 
    ReportNumberOfDeptLeaders(deptLeaders.Count()); 
    if (deptLeaders.Count() > 20) 
     WarnTooManyDepartmentLeaders(deptLeaders.Count()); 
} 

Also, wenn eine Warnung für zu viele Abteilungsleiter wird ausgegeben, .NET geht VIERMAL durch die Personen, prüft sie gegen die Abteilungsleiter, sortiert sie nach Namen und zählt dann die Ergebnisobjekte.

Und dies ist nur, wenn Personen und Abteilungen Preset-Wert-Sammlungen sind, nicht selbst Abfragen.

+0

Ich könnte hinzufügen, dass '.Count()> 0 'ist das gleiche wie' .Any() '. – jedmao

+1

@sfjedi: Ich denke, es ist nicht das Gleiche. Any() stoppt, wenn ein Element gefunden wurde, während Count() durch alle iteriert. Wenn IEnumerable für eine verzögerte Ausführung möglich ist, sollte Any() für die Leerprüfung bevorzugt werden. –

+3

Wäre '.Any()' dann effizienter als '.Count()> 0'? BTW, Resharper beschwert sich immer über '.Count()> 0'. Deshalb bringe ich es zuversichtlich zur Sprache. – jedmao

Verwandte Themen