Kiedy tworzysz metodę rozszerzenia, możesz oczywiście ją wywołać. null
Ale w przeciwieństwie do wywołania metody instancji, wywołanie jej na wartość null nie musi rzucać NullReferenceException
-> musisz to sprawdzić i rzucić ręcznie.
W celu wdrożenia metody rozszerzenia Linq Any()
Microsoft zdecydował, że powinien rzucić ArgumentNullException
( https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/AnyAll.cs ).
Denerwuje mnie to, że muszę pisać if( myCollection != null && myCollection.Any() )
Czy mam rację, jako klient tego kodu, oczekując, że np. ((int[])null).Any()
Powinien zwrócić false
?
c#
.net
linq
extension-method
Rozumiem
źródło
źródło
null |> Seq.isEmpty
rzucaSystem.ArgumentNullException: Value cannot be null
. Wydaje się, że oczekuje się, że nie przekażesz niezdefiniowanych wartości dla czegoś, co ma istnieć, więc nadal jest wyjątkiem, gdy masz wartość null. Jeśli jest to kwestia inicjalizacji, zacznę od pustej sekwencji zamiastnull
.null
gdy masz do czynienia z kolekcjami, ale zamiast tego użyj pustej kolekcji w takich przypadkach.Any
jest po prostu konsekwentny.Odpowiedzi:
Mam torbę z pięcioma ziemniakami. Czy
.Any()
w torbie są ziemniaki?„ Tak ”, mówisz.
<= true
Wyjmuję wszystkie ziemniaki i jem. Czy
.Any()
w torbie są ziemniaki?„ Nie ”, mówisz.
<= false
Całkowicie spalam torbę w ogniu. Czy
.Any()
w torbie są teraz ziemniaki?„ Nie ma torby ”.
<= ArgumentNullException
źródło
Po pierwsze, wygląda na to, że ten kod źródłowy wyrzuci
ArgumentNullException
, a nieNullReferenceException
.To powiedziawszy, w wielu przypadkach już wiesz, że twoja kolekcja nie ma wartości NULL, ponieważ ten kod jest wywoływany tylko z kodu, który wie, że kolekcja już istnieje, więc nie będziesz musiał często umieszczać w nim kontroli null. Ale jeśli nie wiesz, że istnieje, warto sprawdzić to przed wywołaniem
Any()
.Tak. Pytanie, na które
Any()
odpowiedzi brzmi „czy ta kolekcja zawiera jakieś elementy?” Jeśli ten zbiór nie istnieje, samo pytanie jest nonsensowne; nie może ani zawierać ani nie zawierać niczego, ponieważ nie istnieje.źródło
null
jest równoważny zestawowi zawierającemu pusty zestaw (i odwrotnie)?{ null }
i{ {} }
powinieneś być równoważny. Uważam to za fascynujące; W ogóle nie mogę się z tym odnosić..Add
wnull
kolekcji należy w jakiś sposób stworzyć kolekcję dla nas?Null oznacza brakujące informacje, a nie brak elementów.
Można rozważyć bardziej ogólnie unikanie wartości null - na przykład użyj jednego z wbudowanych pustych elementów wyliczeniowych do reprezentowania kolekcji bez elementów zamiast wartości null.
Jeśli w niektórych okolicznościach zwracasz wartość null, możesz to zmienić, aby zwrócić pustą kolekcję. (W przeciwnym razie, jeśli znajdziesz wartości zerowe zwrócone przez metody biblioteczne (nie twoje), to niefortunne, i owinąłbym je, aby się znormalizować.)
Zobacz także https://stackoverflow.com/questions/1191919/what-does-linq-return-when-the-results-are-empty
źródło
null
nie znaczy, że żaden element nie jest kwestią zastosowania rozsądnej konwencji. Pomyśl tylko o natywnych OLESTRINGACH, gdzie tak naprawdę to oznacza.null
nie oznacza to „brakujących informacji”.null
nie ma żadnej znaczącej interpretacji. Zamiast tego chodzi o to, jak to traktujesz.null
jest czasem używany do „brakujących informacji”, ale także do „brak elementów”, a także do „wystąpienia błędu” lub „niezainicjowanej” lub „sprzecznej informacji” lub jakiejkolwiek arbitralnej, ad hoc.null
jest absolutnie często używane w znaczeniu „brak danych”. Czasami ma to sens. Innym razem nie.Oprócz składni zerowej warunkowej istnieje jeszcze jedna technika rozwiązania tego problemu: nie pozwól, aby zmienna pozostała
null
.Rozważ funkcję, która akceptuje kolekcję jako parametr. Jeśli dla celów funkcji
null
i puste są równoważne, możesz upewnić się, że nigdy nie zawieranull
na początku:Możesz zrobić to samo, pobierając kolekcje z innej metody:
(Pamiętaj, że w przypadkach, w których masz kontrolę nad funkcją podobną do pustej kolekcji
GetWords
inull
jest ona równoważna, lepiej jest po prostu zwrócić pustą kolekcję.)Teraz możesz wykonać dowolną operację na kolekcji. Jest to szczególnie przydatne, jeśli trzeba wykonać wiele operacji, które zakończyłyby się niepowodzeniem, gdy kolekcja jest
null
, a w przypadkach, gdy uzyskuje się ten sam wynik przez zapętlenie lub zapytanie pustej listy, pozwoli toif
całkowicie wyeliminować warunki.źródło
Tak, po prostu dlatego, że jesteś w C #, a to zachowanie jest dobrze zdefiniowane i udokumentowane.
Jeśli tworzysz własną bibliotekę lub używasz innego języka z inną kulturą wyjątków, rozsądniej byłoby oczekiwać fałszu.
Osobiście uważam, że zwrot fałszywy jest bezpieczniejszym podejściem, które sprawia, że twój program jest bardziej niezawodny, ale przynajmniej jest dyskusyjny.
źródło
Jeśli denerwują Cię powtarzające się testy zerowe, możesz utworzyć własną metodę rozszerzenia „IsNullOrEmpty ()”, aby utworzyć kopię lustrzaną metody String o tej nazwie i zawinąć zarówno kontrolę zerową, jak i wywołanie .Any () w jedno wywołanie.
W przeciwnym razie rozwiązanie wspomniane przez @ 17 z 26 w komentarzu pod twoim pytaniem jest również krótsze niż metoda „standardowa” i dość zrozumiałe dla każdego, kto zna nową składnię zerową.
źródło
?? false
zamiast== true
. Wydaje mi się to bardziej wyraźne (czystsze), ponieważ obsługuje tylko przypadeknull
i nie jest tak naprawdę bardziej szczegółowe. Plus==
czeki na booleany zwykle wywołują mój odruch wymiotny. =)myCollection?.Any()
wystarczy?myCollection?.Any()
efektywnie zwraca wartość logiczną zerowalną niż zwykłą wartość logiczną (tj. Bool? Zamiast bool). Nie ma niejawnej konwersji z bool? bool, i jest to bool, którego potrzebujemy w tym konkretnym przykładzie (do przetestowania jako częśćif
warunku). Dlatego musimy albo wyraźnie porównaćtrue
lub skorzystać z operatora zerowego koalescencji (??).?.
.==
, czy rzeczywiście zadziała. Już wiem, co się dzieje z Boolean intuicyjnie, kiedy to może być tylkotrue
albofalse
; Nawet nie muszę o tym myśleć. Ale wrzućcienull
do miksu, a teraz muszę ustalić, czy==
to prawda.??
zamiast tego po prostu eliminujenull
skrzynkę, redukując ją z powrotem dotrue
ifalse
, co już natychmiast rozumiesz. Jeśli chodzi o mój odruch wymiotny, kiedy go po raz pierwszy widzę, natychmiast instynktownie go usuwam, ponieważ zwykle jest to bezużyteczne, co nie chce się zdarzyć.Jeśli zastanawiasz się nad oczekiwaniami, musisz pomyśleć o intencjach.
null
oznacza coś zupełnie innego niżEnumerable.Empty<T>
Jak wspomniano w odpowiedzi Erika Eidta , istnieje różnica między znaczeniem
null
a pustą kolekcją.Najpierw rzućmy okiem na to, jak mają być używane.
Książka Framework Design Guidelines: Conventions, Idioms and Patterns for Wielokrotnego użytku bibliotek .NET, 2. wydanie, napisane przez architektów Microsoft, Krzysztofa Cwalinę i Brada Abramsa, podaje następującą najlepszą praktykę:
Rozważ wywołanie metody, która ostatecznie pobiera dane z bazy danych: Jeśli otrzymasz pustą tablicę lub
Enumerable.Empty<T>
oznacza to po prostu, że Twoja próbka jest pusta, tzn. Twój wynik jest pustym zestawem . Odbieranienull
w tym kontekście oznaczałoby jednak stan błędu .W tym samym myśleniu, co analogia ziemniaka Dana Wilsona , sensowne jest zadawanie pytań o dane, nawet jeśli jest to pusty zestaw . Ale ma to o wiele mniej sensu, jeśli nie ma zestawu .
źródło
null
i pusty może skutkować taką samą wartością zwracaną dla funkcji, ale to nie znaczy, żenull
i pusty oznacza dokładnie to samo w zakresie wywoływania.Istnieje wiele odpowiedzi wyjaśniających, dlaczego
null
i puste są różne, oraz wystarczająca liczba opinii próbujących wyjaśnić, dlaczego należy je traktować inaczej, czy nie. Jednak pytasz:Jest to całkowicie uzasadnione oczekiwanie . Jesteś jak prawo jak ktoś opowiada o obecnym zachowaniu. Zgadzam się z obecną filozofią wdrażania, ale czynniki napędzające są nie tylko oparte na względach poza kontekstem.
Biorąc pod uwagę, że
Any()
bez predykatu jest zasadniczoCount() > 0
to, czego oczekujesz od tego fragmentu?Lub ogólny:
Przypuszczam, że się spodziewasz
NullReferenceException
.Any()
jest metodą rozszerzenia, powinna płynnie integrować się z rozszerzonym obiektem, a następnie zgłoszenie wyjątku jest najmniej zaskakującą rzeczą.Enumerable.Any(null)
i tam na pewno się spodziewaszArgumentNullException
. Jest to ta sama metoda i musi być zgodna z - być może - prawie WSZYSTKIM innym w ramach.null
obiektu jest błędem programistycznym , środowisko nie powinno wymuszać wartości null jako magicznej wartości . Jeśli użyjesz go w ten sposób, musisz sobie z tym poradzić.Count()
wydaje się to łatwa decyzja,Where()
nie jest. CoMax()
? Zgłasza wyjątek dla listy PUSTEJ, czy nie powinno to również wyrzucić dlanull
jednego?To, co projektanci bibliotek zrobili przed LINQ, polegało na wprowadzeniu jawnych metod, gdy
null
jest to prawidłowa wartość (na przykładString.IsNullOrEmpty()
), wtedy MUSI być spójne z istniejącą filozofią projektowania . To powiedziawszy, nawet jeśli dość proste do napisania, dwie metodyEmptyIfNull()
iEmptyOrNull()
mogą się przydać.źródło
Jim powinien zostawiać ziemniaki w każdej torbie. W przeciwnym razie go zabiję.
Jim ma worek z pięcioma ziemniakami. Czy
.Any()
w torbie są ziemniaki?„Tak”, mówisz. <= prawda
Ok, więc tym razem Jim żyje.
Jim bierze wszystkie ziemniaki i je je. Czy w torbie są jakieś. () Ziemniaki?
„Nie”, mówisz. <= fałsz
Czas zabić Jima.
Jim całkowicie spala torbę w ogniu. Czy w torbie są teraz jakieś. () Ziemniaki?
„Nie ma torby”. <= ArgumentNullException
Czy Jim powinien żyć czy umrzeć? Nie spodziewaliśmy się tego, więc potrzebuję orzeczenia. Czy pozwolenie Jimowi na ucieczkę to błąd, czy nie?
Możesz użyć adnotacji, aby zasygnalizować, że nie znosisz w ten sposób żadnych zerowych shenaniganów.
Ale Twój łańcuch narzędzi musi to obsługiwać. Co oznacza, że prawdopodobnie nadal będziesz pisać czeki.
źródło
Jeśli tak bardzo Ci to przeszkadza, proponuję prostą metodę rozszerzenia.
Teraz możesz to zrobić:
... a flaga zostanie ustawiona na
false
.źródło
return
oświadczenia w następujący sposób:return source ?? Enumerable.Empty<T>();
To jest pytanie dotyczące metod rozszerzenia C # i ich filozofii projektowania, więc myślę, że najlepszym sposobem na odpowiedź na to pytanie jest zacytowanie dokumentacji MSDN na temat metod rozszerzenia :
Podsumowując, metody rozszerzeń służą do dodawania metod instancji do określonego typu, nawet jeśli programiści nie mogą tego zrobić bezpośrednio. A ponieważ metody instancji zawsze zastępują metody rozszerzeń, jeśli są obecne (jeśli są wywoływane przy użyciu składni metody instancji), należy to zrobić tylko wtedy, gdy nie można bezpośrednio dodać metody lub rozszerzyć klasy. *
Innymi słowy, metoda rozszerzenia powinna działać tak samo, jak metoda instancji, ponieważ może być w końcu metodą instancji stworzoną przez jakiegoś klienta. A ponieważ metoda instancji powinna rzucić, jeśli wywoływany jest obiekt
null
, podobnie jak metoda rozszerzenia.* Na marginesie, jest to dokładnie taka sytuacja, że projektanci z LINQ do czynienia: gdy C # 3.0 został wydany, były już miliony klientów, którzy używali
System.Collections.IEnumerable
iSystem.Collections.Generic.IEnumerable<T>
, zarówno w swoich zbiorach iforeach
pętle. Klasy te zwróconeIEnumerator
przedmioty, które miał tylko dwie metodyCurrent
iMoveNext
, więc dodanie dodatkowych wymaganych metod przykład, takie jakCount
,Any
itd byłoby złamanie tych milionów klientów. Tak więc, w celu dostarczenia tej funkcji (zwłaszcza, że to może być realizowane w warunkachCurrent
iMoveNext
ze względną łatwością), wydali go jako metod rozszerzeń, które mogą być stosowane do każdego obecnie istniejącychIEnumerable
wystąpienie i może być również zaimplementowane przez klasy w bardziej wydajny sposób. Gdyby projektanci C # zdecydowali się wydać LINQ pierwszego dnia, byłby on dostarczony jako metoda instancjiIEnumerable
i prawdopodobnie zaprojektowaliby jakiś system zapewniający domyślne implementacje interfejsu tych metod.źródło
Myślę, że istotną kwestią jest to, że zwracając wartość false zamiast zgłaszania wyjątku, zaciemniasz informacje, które mogą być istotne dla przyszłych czytelników / modyfikatorów twojego kodu.
Jeśli możliwe jest, aby lista była pusta, sugerowałbym, aby mieć do tego osobną ścieżkę logiczną, ponieważ w przeciwnym razie ktoś może dodać logikę opartą na liście (np. Dodać) do else {} twojego if, w wyniku czego niechciany wyjątek, którego nie mieli powodu przewidzieć.
Czytelność i łatwość konserwacji przeważają „muszę napisać dodatkowy warunek” za każdym razem.
źródło
Zasadniczo piszę większość mojego kodu, aby założyć, że dzwoniący jest odpowiedzialny za to, że nie przekazuje mi zerowych danych, głównie z powodów opisanych w powiedz „ nie” zeru (i innym podobnym postom). Pojęcie wartości null często nie jest dobrze rozumiane iz tego powodu wskazane jest zainicjowanie zmiennych przed czasem, o ile jest to praktyczne. Zakładając, że pracujesz z rozsądnym API, najlepiej nie powinieneś nigdy odzyskiwać wartości zerowej, więc nigdy nie powinieneś sprawdzać, czy jest pusta. Punkt zerowy, jak stwierdzono w innych odpowiedziach, to upewnienie się, że masz prawidłowy obiekt (np. „Torbę”) do pracy przed kontynuowaniem. To nie jest cecha języka
Any
, ale funkcja językasamo. Nie można wykonać większości operacji na obiekcie zerowym, ponieważ on nie istnieje. To tak, jakby poprosić cię, abyś pojechał do sklepu w moim samochodzie, z wyjątkiem tego, że nie posiadam samochodu, więc nie możesz użyć mojego samochodu, aby pojechać do sklepu. Bezsensowne jest wykonywanie operacji na czymś, co dosłownie nie istnieje.Istnieją praktyczne przypadki używania wartości zerowej, na przykład gdy dosłownie nie masz wymaganych informacji (np. Gdybym zapytał cię, jaki jest mój wiek, najbardziej prawdopodobnym wyborem, na który możesz odpowiedzieć w tym momencie, jest „nie wiem ”, co jest wartością zerową). Jednak w większości przypadków powinieneś przynajmniej wiedzieć, czy twoje zmienne zostały zainicjowane czy nie. A jeśli nie, to zalecam zaostrzenie kodu. Pierwszą rzeczą, którą robię w dowolnym programie lub funkcji, jest zainicjowanie wartości przed jej użyciem. Rzadko muszę sprawdzać wartości null, ponieważ mogę zagwarantować, że moje zmienne nie mają wartości null. Dobrym nawykiem jest wchodzenie w to, a im częściej pamiętasz, aby zainicjować zmienne, tym rzadziej musisz sprawdzać, czy nie ma wartości null.
źródło