Jakie jest zastosowanie .Any () na liście C # <>?

40

Byłem omawiania tego z kolegami, a nie mogliśmy dowiedzieć się, co ma zastosowanie .Anydla danego List<>, w języku C #.

Możesz sprawdzić poprawność elementu w tablicy, podobnie jak następująca instrukcja:

if (MyList.Any()){ ...}  //Returns true or false

Który jest dokładnie taki sam jak

if (MyList.Count() != 0) { ... }

i jest znacznie bardziej powszechny, czytelny i jasny na temat intencji tego ifstwierdzenia.

W końcu utknęliśmy z tą myślą:

.Any() może być używany, będzie działał równie dobrze, ale nie jest jasne, co do intencji programisty, i w takim przypadku nie należy go używać.

Ale czujemy, że to nie może być prawda; coś nam brakuje.

Jesteśmy?

Gil Sand
źródło
40
w jaki sposób cel jest mniej jasny?
jk.
35
Podważam twoje twierdzenie, które Any()jest mniej jasne: Any()wydaje mi się bardziej jasne, szczególnie w przypadku lambda. Tłumaczenie kodu na angielski w mojej głowie if(MyList.Count(o => o > 10) > 0)brzmi: „Czy liczba pozycji większa niż 10 jest większa niż 0?” mając na if(MyList.Any(o => o > 10))myśli „Czy są jakieś przedmioty większe niż 10?”
BlueRaja - Danny Pflughoeft
3
@SargeBorsch - tylko jeśli wolisz Heskel / funkcjonalne nazewnictwo, czego większość ludzi nie lubi.
Davor Ždralo,
4
@ BlueRaja-DannyPflughoeft Zgadzam się. Myślę, że wszystko jest bardziej jasne, ponieważ eliminuje coś, co można by uznać za liczbę magiczną.
Andy,
2
@SargeBorsch: SQL równolegle do Anyto Exists. Nazwa Linq jest prawdopodobnie bardziej zainspirowana przez Haskell i Python, które mają również dowolne / wszystkie funkcje.
JacquesB,

Odpowiedzi:

95

Pamiętaj, że Anynie działa na List; działa na IEnumerable, który reprezentuje konkretny typ, który może mieć Countwłaściwość lub nie . To prawda, że ​​niekoniecznie jest to najlepsza rzecz do użycia List, ale na pewno przydaje się na końcu zapytania LINQ. Jeszcze bardziej przydatne niż wersja autonomiczna jest zastąpienie, które podobnie jak predykat Where. Nie ma nic wbudowanego List, co byłoby tak wygodne lub wyraziste, jak Anymetoda rozszerzenia predykatu .

Ponadto, jeśli używasz Count()(metoda rozszerzenia LINQ dla IEnumerable) zamiast Count(właściwość on List), może być konieczne wyliczenie całej sekwencji, jeśli nie można jej zoptymalizować, wykrywając, że Twój typ danych ma Countwłaściwość . Jeśli masz długą sekwencję, może to być zauważalny spadek wydajności, gdy tak naprawdę nie zależy ci na tym, co się liczy, i po prostu chcesz wiedzieć, czy w kolekcji są jakieś elementy.

Mason Wheeler
źródło
19
To. Pomijając wydajność, Any()z predykatem jest bardziej ekspresyjny niż porównywanie tego, Enumerable.Count()co ma predykat z 0. :)
Dan J
1
Myślę, że to wyjaśnia podstawy tak jasno, że najlepiej wyjaśnia odpowiedź.
Tarik
2
IMHO Existsjest tak samo wygodny i wyrazisty jak Anyz predykatem. Używanie Count != 0właściwości na Listjest bardziej normalne niż używanie Any(). To tylko osobiste preferencje. Ja również przeszedł wysiłku zmienia _list_.Count()się _list_.Countw kodzie mojej grupy. To zrobiło dla mnie zauważalną różnicę.
Suncat2000
55

Różnica czasu pracy Count()może wynosić O (n), gdzie Any()jest O (1).

Znak
źródło
15
Count()nie zatrzyma się także dla nieskończonych iteratorów, podczas gdy Any()będzie, ponieważ wystarczy zadzwonić MoveNext()tylko raz.
Jon Purdy,
3
Zwięzłe i dokładne, ale brzmi to bardziej jako komentarz niż odpowiedź. Programiści preferują odpowiedzi, które zagłębiają się w przyczyny i dostarczają wyjaśnienia. Rozważ edycję swojej odpowiedzi i wyjaśnienie, dlaczego O (1) może być (jest) ważne.
bardzo dobra odpowiedź właśnie się nauczyłem, że powinienem użyć dowolnej zamiast liczyć == 0: d
MonsterMMORPG
4
Any(Func<T>)jest O (n)
BlueRaja - Danny Pflughoeft
1
W konkretnym przypadku Listwydajność jest taka sama, ponieważ rozszerzenie korzysta z właściwości. Prawda dla wszystkich kolekcji implementujących ICollectioninterfejs.
Thorkil Holm-Jacobsen
9

Należy pamiętać, że istnieje właściwość List.Count , a następnie metoda Enumerable.Count .

W twoim przykładzie używasz Enumerable.Count()metody, która musi iterować każdy element wyliczenia, aby zwrócić wynik. Jest to wyraźnie wolniejsze niż sprawdzanie, Any()które musi powtarzać tylko pierwszy element, jeśli istnieje.

EDYTOWAĆ:

W komentarzach wskazano słusznie, że Enumerable.Count()metoda rozszerzenia nie musi iterować wszystkich elementów, jeśli wykryje, że wyliczenie również się zdarza ICollection<T>. Tak więc w przypadku a List<T>użycie Countwłaściwości lub metody nie robi żadnej różnicy.

Źródło IEnumerable.Count :

public static int Count<TSource>(this IEnumerable<TSource> source) {
    if (source == null) throw Error.ArgumentNull("source");
    ICollection<TSource> collectionoft = source as ICollection<TSource>;
    if (collectionoft != null) return collectionoft.Count;
    ICollection collection = source as ICollection;
    if (collection != null) return collection.Count;
    int count = 0;
    using (IEnumerator<TSource> e = source.GetEnumerator()) {
        checked {
            while (e.MoveNext()) count++;
        }
    }
    return count;
}
sstan
źródło
Słusznie. Co zatem z użyciem właściwości .Count? czy nie sprawiłoby to, że odczyt byłby znacznie szybszy, może tak szybki lub nawet szybszy niż wywołanie .Any ()?
Gil Sand,
4
Z List<T>różnicą wydajności będzie znikoma. Więc po prostu użyj tego, co wolisz. Ale w przypadku innych typów IEnumerables możesz jedynie wybierać pomiędzy Count()(metodą), Any()aw takim przypadku Any()zawsze będzie wyraźnym zwycięzcą dla twojego przypadku użycia i powinno być preferowane. Co do tego, co jest warte, myślę, że Any()jest dość czytelny i jasny.
sstan
@ stan tak, chyba że jeśli licznik można rzucić w kolaż, można go zoptymalizować i nie będzie to miało znaczenia.
Esben Skov Pedersen
2
Count()ma taką samą złożoność jak Countdla ICollections, ponieważ metoda rozszerzenia używa właściwości zamiast iteracji.
Thorkil Holm-Jacobsen
Rozszerzenia Linq są zoptymalizowane dla znanych interfejsów - IList, ICollection. Dyskusja na temat wydajności jest tutaj całkowicie zbędna, nawet jeśli jest to szczegół implementacyjny
Gusdor,
6

Zaskakujące pytanie - uważam, że intencja jest o list.Any()wiele jaśniejsza niż intencja list.Count()!=0.

Cel: oznacza, że ​​jeśli czytasz czyjś kod (a sam go nie napisałeś), czy jest całkowicie oczywiste, co programista chce osiągnąć i dlaczego napisał tak, jak jest? Jeśli wspólny problem zostanie rozwiązany w niepotrzebnie skomplikowany sposób, natychmiast podejrzewasz i zastanawiasz się, dlaczego programista nie zastosował prostej metody. Przeglądasz kod i próbujesz sprawdzić, czy coś przeoczyłeś. Boisz się zmienić kod, ponieważ obawiasz się, że brakuje jakiegoś efektu ubocznego, a jego zmiana może spowodować nieoczekiwane problemy.

Cel użycia tej Any()metody jest całkowicie jasny - chcesz wiedzieć, czy są jakieś elementy na liście, czy nie.

Z Count()!=0drugiej strony cel wyrażenia nie jest oczywisty dla czytelnika. Oczywiście nietrudno zauważyć, że wyrażenie informuje, czy lista jest pusta, czy nie. Pytania powstają, ponieważ piszesz je w ten konkretny sposób, zamiast używać standardowej metody. Dlaczego używasz Count()jawnie? Jeśli naprawdę potrzebujesz tylko wiedzieć, czy na liście znajdują się jakieś elementy, dlaczego najpierw chcesz policzyć całą listę? Jak tylko osiągniesz 1, masz już swoją odpowiedź. Jeśli źródło było iteratorem dużej (być może nieskończonej) kolekcji lub zostało przetłumaczone na sql, mogłoby to mieć duży wpływ na wydajność. Może więc jawnym zastosowaniem Count () jest wymuszenie wykonania odroczonego zapytania w celu wykonania iteratora?

Ale źródło jest w rzeczywistości List<T>gdzie Count()jest O (1) i nie ma skutków ubocznych. Ale jeśli kod opiera się na tej właściwości List<T>, to dlaczego nie użyć Countwłaściwości, która wyraźniej wskazuje, że oczekujesz operacji O (1) bez efektów ubocznych?

Jak napisano, list.Count()!=0robi dokładnie to samo, co list.Any()poza tym, że jest niepotrzebnie bardziej skomplikowane, a intencja jest niejasna.

JacquesB
źródło
Przeczytałem oba jako: „Czy lista zawiera jakieś elementy?” Count()ma domyślną konwersję, której chciałbym uniknąć, ale nie jest niejasne. Kompilator może go zoptymalizować, co czyni go nieostrożnym kodowaniem.
Suncat2000
2

Może to tylko słowo? Anyjest przymiotnikiem, tak naprawdę nic nie mówiąc. Pochodzę z Javy, nazwałbym to isNonEmptyczasownikiem. Facet z SQL może preferować EXISTS. Ale może Anynajlepiej pasuje do systemu C #.

Niezależnie od wyboru słowa, po przyzwyczajeniu się do niego, musi być znacznie jaśniejsze. Czy zapytasz „Czy zostały more than zerobutelki piwa”. Czy spodziewałbyś się, że one or moreosoby zaczną je liczyć, zanim odpowiedzą „Nie, nie ma any”?

maaartinus
źródło
1
Nazwa „dowolny” ma sens, gdy myślisz o tym w kategoriach LINQ: Any()to naprawdę skrót doAny(o => true)
BlueRaja - Danny Pflughoeft
Zwykle nie użyłbym pustego opisu, jeśli wyliczalny miałby coś do wyliczenia. Ale „to nie lada rzecz wyliczyć” wydaje mi się naturalne, a każda działa przeciwko jakiejkolwiek wyliczenie.
Andy,