Właściwość Count a metoda Count ()?

85

Pracując z kolekcją, mam dwa sposoby uzyskania liczby obiektów; Count(właściwość) i Count()(metoda). Czy ktoś wie, jakie są kluczowe różnice?

Mogę się mylić, ale zawsze używam tej Countwłaściwości w jakichkolwiek instrukcjach warunkowych, ponieważ zakładam, że Count()metoda wykonuje jakieś zapytanie względem kolekcji, gdzie Countmusiało być już przypisane przed moim „pobieraniem”. Ale to przypuszczenie - nie wiem, czy jeśli się mylę, wpłynie to na wydajność.

EDYCJA: Więc z ciekawości Count()wyrzuci wyjątek, jeśli kolekcja jest pusta? Ponieważ jestem prawie pewien, że Countwłaściwość po prostu zwraca 0.

d219
źródło
7
Oba będą zgłaszać wyjątek dla kolekcji o wartości null, ponieważ obie próbują zastosować .operator do czegoś, co ma wartość null.
AaronLS

Odpowiedzi:

109

Dekompilacja źródła dla Count()metody rozszerzenia ujawnia, że ​​sprawdza, czy obiekt jest ICollection(generyczny lub inny), a jeśli tak, po prostu zwraca Countwłaściwość bazową :

Tak więc, jeśli twój kod uzyskuje dostęp Countzamiast wywoływać Count(), możesz ominąć sprawdzanie typu - teoretyczna korzyść z wydajności, ale wątpię, czy byłaby zauważalna!

// System.Linq.Enumerable
public static int Count<TSource>(this IEnumerable<TSource> source)
{
    checked
    {
        if (source == null)
        {
            throw Error.ArgumentNull("source");
        }
        ICollection<TSource> collection = source as ICollection<TSource>;
        if (collection != null)
        {
            return collection.Count;
        }
        ICollection collection2 = source as ICollection;
        if (collection2 != null)
        {
            return collection2.Count;
        }
        int num = 0;
        using (IEnumerator<TSource> enumerator = source.GetEnumerator())
        {
            while (enumerator.MoveNext())
            {
                num++;
            }
        }
        return num;
    }
}
Ian Nelson
źródło
10
+1 za przejęcie inicjatywy i odtworzenie tego, bardzo pomocne.
Wielomian
7
Należy jednak pamiętać, że wersja 3.5 Count()nie sprawdza ICollectioninterfejsu nieogólnego . Zostało to dodane tylko w .NET 4. Zarówno wersja 3.5, jak i 4 sprawdzają ICollection<T>interfejs ogólny .
thecoop
2
wywołanie funkcji count na sekwencji bez elementów spowoduje zgłoszenie wyjątku. Ale Count () będzie działał dobrze.
amesh
33

Wydajność to tylko jeden z powodów, dla których warto wybrać jedną z nich. Wybór .Count()oznacza, że ​​Twój kod będzie bardziej ogólny. Zdarzały się sytuacje, w których refaktoryzowałem kod, który nie tworzył już kolekcji, ale zamiast tego coś bardziej ogólnego, jak IEnumerable, ale inny kod zepsuł się w wyniku, ponieważ zależał od .Counti musiałem go zmienić .Count(). Gdybym chciał używać go .Count()wszędzie, kod prawdopodobnie byłby łatwiejszy do ponownego wykorzystania i utrzymania. Zazwyczaj najlepszym rozwiązaniem jest skorzystanie z bardziej ogólnych interfejsów, jeśli możesz sobie z tym poradzić. Przez bardziej ogólny rozumiem prostszy interfejs, który jest zaimplementowany przez więcej typów, a tym samym zapewnia większą zgodność między kodem.

Nie mówię, że .Count()jest lepsze, mówię tylko, że istnieją inne kwestie, które dotyczą bardziej ponownego wykorzystania kodu, który piszesz.

AaronLS
źródło
2
+1 Cenny dodatek do dyskusji. Część kodu, który utrzymuję, po prostu się zepsuła, ponieważ właściwość .Count nie przetrwała aktualizacji wersji HtmlAgilityPack.
Dan Solovay
1
To może być miecz obosieczny. A co, jeśli pewnego dnia ktoś spróbuje zmodyfikować IEnumerable, aby był prawdziwym generatorem. Patrząc na bazę kodu, widzę wiele miejsc, w których .Count () zakłada, że ​​wyliczenie może być wielokrotnie iterowane
bashrc
@bashrc True. Myślę, że jeśli rozważamy zmianę kodu programisty w porównaniu ze zmianą kodu frameworka, bardziej prawdopodobne jest, że kod programisty ulegnie zmianie. Gdyby tego rodzaju zmiana została wprowadzona w ramach, zepsułoby wiele rzeczy. Tradycyjnie wprowadzają nowe kolekcje / interfejsy w takich przypadkach, aby programiści mogli migrować zgodnie z potrzebami.
AaronLS
20

.Count()Metoda może być na tyle sprytny, lub wie o rodzaju produktu, a jeśli tak, to może skorzystać z podstawowych .Countwłasności.

Ale może nie.

Powiedziałbym, że można bezpiecznie założyć, że jeśli kolekcja ma .Countsamą właściwość, będzie to najlepszy wybór, jeśli chodzi o wydajność.

Jeśli .Count()metoda nie wie o kolekcji, wyliczy ją, co będzie operacją O (n).

Lasse V. Karlsen
źródło
3
Używanie Countwłaściwości mogłoby być lepsze, ale Count()tworzenie metod przy użyciu ICollection<T>.Countjest trochę udokumentowane tutaj: msdn.microsoft.com/en-us/library/bb338038(v=vs.110).aspx
nawfal
Nie wiem, skąd wiesz prawie wszystko.
dniu
5

Count()metoda jest metodą rozszerzającą, która iteruje każdy element an IEnumerable<>i zwraca liczbę elementów. Jeśli instancja IEnumerablejest faktycznie a List<>, jest więc zoptymalizowana pod kątem zwracania Countwłaściwości zamiast iterowania wszystkich elementów.

AntonioR
źródło
nawet jeśli mam List <>, używam metody Count (), aby mój kod był bardziej ogólny. Dobrze, gdy refaktoryzuję moje klasy, aby używać IEnumerable <>, gdzie nie ma potrzeby implementacji określonej kolekcji.
AntonioR
3

Count()istnieje jako metoda rozszerzenia z LINQ - Countjest właściwością na Lists, rzeczywistych obiektach kolekcji .NET.

Jako taki, Count()prawie zawsze będzie wolniejszy, ponieważ wylicza kolekcję / obiekt podlegający zapytaniom. Na liście, kolejce, stosie itp Count. Użyj . Lub dla tablicy - Length.

Kieren Johnstone
źródło
3

Wersja skrócona: Jeśli masz wybór między Countwłaściwością a plikiemCount() metodą, zawsze wybieraj właściwość.

Różnica polega głównie na wydajności operacji. Wszystkie kolekcje BCL, które ujawniają Countwłaściwość, robią to w sposób O (1). Jednak Count()metoda ta może i często będzie kosztować O (N). Istnieje kilka testów, które można spróbować doprowadzić do O (1) dla niektórych implementacji, ale w żadnym wypadku nie jest to gwarantowane.

JaredPar
źródło
Myślę, że ta odpowiedź jest bardziej rozsądne wykorzystanie właściwości Count
AT
3

Jeśli istnieje właściwość Countlub Length, zawsze powinieneś preferować tę Count()metodę niż metodę, która generalnie iteruje całą kolekcję, aby policzyć liczbę elementów w niej zawartych. Wyjątkami byłyby sytuacje, w których Count()metoda jest skierowana przeciwko źródłu LINQ to SQL lub LINQ to Entities, na przykład w takim przypadku wykona zapytanie zliczające względem źródła danych. Nawet wtedy, jeśli istnieje Countnieruchomość, wolisz ją, ponieważ prawdopodobnie ma mniej pracy.

Dave C.
źródło
3

Ta Count()metoda jest metodą LINQ, która działa na dowolnym IEnumerable<>. Można oczekiwać, żeCount() metoda będzie iterować całą kolekcję, aby znaleźć liczbę, ale uważam, że kod LINQ faktycznie zawiera pewne optymalizacje, aby wykryć, czy istnieje właściwość Count, a jeśli tak, użyj jej.

Więc obaj powinni robić prawie identyczne rzeczy. Właściwość Count jest prawdopodobnie nieco lepsza, ponieważ nie ma tam potrzeby sprawdzania typu.

Dylan Smith
źródło