Czy nie jest uzasadnione oczekiwanie, że Any () * not * zgłosi wyjątek o wartości zerowej?

41

Kiedy tworzysz metodę rozszerzenia, możesz oczywiście ją wywołać. nullAle 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?

Rozumiem
źródło
41
w C # 6 możesz uprościć sprawdzenie, czy (myCollection? .Any () == true)
17 z 26
5
Nawet w F #, który tak naprawdę nie używa zer, z wyjątkiem współdziałania z innymi językami .NET, null |> Seq.isEmptyrzuca System.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 zamiast null.
Aaron M. Eshbach,
21
Dlatego nie powinieneś wracać, nullgdy masz do czynienia z kolekcjami, ale zamiast tego użyj pustej kolekcji w takich przypadkach.
Bakuriu
31
Dlaczego w ogóle jest zerowy? Nie chcesz, aby wartość null była liczona jako pusta, ponieważ nie jest pusta, to coś zupełnie innego. Być może chcesz, aby w twoim przypadku było to puste, ale dodanie tego rodzaju rzeczy do języka prowadzi do błędów.
user253751,
22
Powinienem zaznaczyć, że każda metoda LINQ generuje argument zerowy. Anyjest po prostu konsekwentny.
Sebastian Redl,

Odpowiedzi:

157

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

Dan Wilson
źródło
3
Ale próżno, nie ma ziemniaków w torbie? Fałsz oznacza Fałsz ... (nie zgadzam się, tylko druga perspektywa).
D. Ben Knoble,
6
bardziej praktycznie, ktoś wręcza ci zamknięte pudełko z nieznanego źródła. Czy w torbie w pudełku są ziemniaki? Interesują mnie tylko 2 scenariusze - A) w pudełku jest torba z ziemniakami, lub B) w pudełku nie ma ziemniaków i / lub torby. Semantycznie, tak, istnieje różnica między wartością zerową a pustą, ale 99% czasu mnie to nie obchodzi.
dave thieben
6
Zjadłeś pięć surowych ziemniaków? Natychmiast wezwać pomoc medyczną.
Oly,
3
@Oly Dan ugotował je w ogniu, zanim zjadł i użył tego ognia do spalenia torby.
Ismael Miguel
3
@ D.BenKnoble: Czy nie widziałeś bagażnika mojego samochodu, czy chcesz zeznawać, że w bagażniku mojego samochodu nie ma żadnych ciał? Nie? Jaka jest różnica między sprawdzaniem pnia a nie znajdowaniem niczego lub nie sprawdzaniem pnia? Nie możesz po prostu zeznawać teraz, ponieważ w obu przypadkach widziałbyś dokładnie taką samą liczbę ciał? ... to różnica. Nie możesz poręczyć za coś, jeśli nie jesteś w stanie tego potwierdzić. Zakładasz, że oba są możliwe do zrównania, ale to nie jest pewne. W niektórych przypadkach może istnieć istotna różnica między nimi.
Flater
52

Po pierwsze, wygląda na to, że ten kod źródłowy wyrzuci ArgumentNullException, a nie NullReferenceException.

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().

Czy mam rację, jako klient tego kodu, oczekując, że np. ((int[])null).Any()Powinien zwrócić false?

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.

Mason Wheeler
źródło
18
@ChrisWohlert: Twoja intuicja mnie intryguje. Czy uważasz również, że semantycznie nieistniejąca osoba jest pustą osobą, której imię to pusty ciąg, a wiek to zero i tak dalej? A czy uważasz, że zestaw zawierający nulljest równoważny zestawowi zawierającemu pusty zestaw (i odwrotnie)?
ruakh
17
@ChrisWohlert: Oczywiście zestaw może zawierać pusty zestaw; ale wydaje się, że w to wierzysz { null }i { {} }powinieneś być równoważny. Uważam to za fascynujące; W ogóle nie mogę się z tym odnosić.
ruakh
9
Czy .Addw nullkolekcji należy w jakiś sposób stworzyć kolekcję dla nas?
Matthew
13
@ChrisWohlert "A jest pustym zestawem. B jest zestawem zawierającym arbuza. Czy A jest pusty? Tak. Czy B jest pusty? Nie. Czy C jest pusty? Uhhh ..."
253751
16
Pedantyczny punkt: nie jest tak, że w teorii zbiorów nieistniejący zbiór jest zbiorem pustym. Pusty zestaw istnieje, a każdy nieistniejący zestaw nim nie jest (i w rzeczywistości nie jest niczym, ponieważ nie istnieje).
James_pic,
22

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

Erik Eidt
źródło
1
To nullnie znaczy, że żaden element nie jest kwestią zastosowania rozsądnej konwencji. Pomyśl tylko o natywnych OLESTRINGACH, gdzie tak naprawdę to oznacza.
Deduplicator,
1
@Deduplicator, czy mówisz o możliwościach Microsoft Object Linking i Embedding? Przypomnijmy, że OLE pochodzi z lat 90.! Patrząc obecnie, wiele współczesnych języków (C #, Java, Swift) zapewnia narzędzia do eliminacji / eliminacji użycia wartości null.
Erik Eidt,
2
@ErikEidt Robią to częściowo, ponieważ nullnie oznacza to „brakujących informacji”. nullnie ma żadnej znaczącej interpretacji. Zamiast tego chodzi o to, jak to traktujesz. nulljest 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.
Derek Elkins
-1. To decyzja podjęta przez programistę, a nie abstrakcyjne teorie. nulljest absolutnie często używane w znaczeniu „brak danych”. Czasami ma to sens. Innym razem nie.
jpmc26
13

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 nulli puste są równoważne, możesz upewnić się, że nigdy nie zawiera nullna początku:

public MyResult DoSomething(int a, IEnumerable<string> words)
{
    words = words ?? Enumerable.Empty<string>();

    if (!words.Any())
    {
        ...

Możesz zrobić to samo, pobierając kolekcje z innej metody:

var words = GetWords() ?? Enumerable.Empty<string>();

(Pamiętaj, że w przypadkach, w których masz kontrolę nad funkcją podobną do pustej kolekcji GetWordsi nulljest 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 to ifcałkowicie wyeliminować warunki.

jpmc26
źródło
12

Czy się mylę, jako klient tego kodu, oczekując, że np. ((Int []) null) .Każdy () powinien zwrócić wartość false?

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.

Telastyn
źródło
15
Ta „niezawodność” jest często iluzją - jeśli kod generujący tablicę nagle zacznie generować wartości NULL niespodziewanie z powodu błędu lub innego problemu, Twój „solidny” kod ukryje problem. Czasami jest to właściwe zachowanie, ale nie jest to bezpieczne ustawienie domyślne.
GoatInTheMachine,
1
@GoatInTheMachine - Nie zgadzam się, że jest to bezpieczne ustawienie domyślne, ale masz rację, że to ukrywa problem i że takie zachowanie jest czasami niepożądane. Z mojego doświadczenia wynika, że ​​ukrywanie problemu (lub ufanie testom jednostkowym w celu wykrycia problemów z innym kodem) jest lepsze niż zawieszanie aplikacji przez zgłaszanie nieoczekiwanych wyjątków. Mam na myśli naprawdę - jeśli kod wywołujący jest wystarczająco uszkodzony, aby wysłać wartości zerowe, jaka jest szansa, że ​​poprawnie obsługują wyjątki?
Telastyn
10
Jeśli coś jest zepsute, czy to mój kod, kod innego kolegi czy klienta, wolałbym, żeby kod natychmiast się nie powiódł i ludzie wiedzieli o nim, niż działał w potencjalnie niespójnym stanie przez nieokreślony czas. Możliwość zrobienia tego jest oczywiście czasem luksusem i nie zawsze jest odpowiednia, ale takie podejście preferuję.
GoatInTheMachine,
1
@GoatInTheMachine - Zgadzam się na wiele rzeczy, ale kolekcje bywają różne. Traktowanie wartości null jako pustej kolekcji nie jest strasznie niespójnym lub zaskakującym zachowaniem.
Telastyn,
8
Wyobraź sobie, że masz tablicę wypełnioną z dokumentu JSON otrzymanego za pośrednictwem żądania internetowego, a podczas produkcji jeden z klientów interfejsu API nagle ma problem z wysyłaniem przez częściowo nieprawidłowy JSON, co powoduje, że deserializujesz tablicę jako NULL. kod, w najgorszym przypadku, zgłosił wyjątek NULL w momencie pojawienia się pierwszego złego żądania, co zobaczysz w logowaniu, a następnie natychmiast powiesz klientowi, aby naprawił ich zepsute żądania, LUB twój kod mówi „och, tablica jest pusta, nic do zrobienia!" i po cichu napisał do koszyka „koszyk nie miał żadnych przedmiotów” przez kilka tygodni?
GoatInTheMachine
10

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ą.

if(myCollection?.Any() == true)
Theo Brinkman
źródło
4
Możesz także użyć ?? falsezamiast == true. Wydaje mi się to bardziej wyraźne (czystsze), ponieważ obsługuje tylko przypadek nulli nie jest tak naprawdę bardziej szczegółowe. Plus ==czeki na booleany zwykle wywołują mój odruch wymiotny. =)
jpmc26,
2
@ jpmc26: Nie wiem dużo o C #. Dlaczego nie myCollection?.Any()wystarczy?
Eric Duminil,
6
@EricDuminil Z powodu sposobu, w jaki działa operator warunkowy o wartości zerowej, 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ęść ifwarunku). Dlatego musimy albo wyraźnie porównać truelub skorzystać z operatora zerowego koalescencji (??).
Steven Rands,
@ jpmc26 ?? false jest trudniejszy do odczytania niż myCollection? .Any () == true. Bez względu na odruch wymiotny, == true jest tym, co próbujesz niejawnie przetestować. ?? false po prostu dodaje dodatkowy hałas i nadal musisz dokonać oceny, co się stanie, jeśli null, to null == true by się nie powiodło, prawie bez czytelnika nawet tego świadomego ?..
Ryan The Leach
2
@RyanTheLeach Muszę się dużo bardziej zastanowić ==, czy rzeczywiście zadziała. Już wiem, co się dzieje z Boolean intuicyjnie, kiedy to może być tylko truealbo false; Nawet nie muszę o tym myśleć. Ale wrzućcie nulldo miksu, a teraz muszę ustalić, czy ==to prawda. ??zamiast tego po prostu eliminuje nullskrzynkę, redukując ją z powrotem do truei false, 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ć.
jpmc26,
8

Czy się mylę, jako klient tego kodu, oczekując, że np. ((Int []) null) .Każdy () powinien zwrócić wartość false?

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 nulla 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ę:

X NIE zwracaj wartości pustych z właściwości kolekcji lub z metod zwracających kolekcje. Zamiast tego zwraca pustą kolekcję lub pustą tablicę.

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 . Odbieranie nullw 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 .

Søren D. Ptæus
źródło
1
To, czy mają różne znaczenia, zależy w dużym stopniu od kontekstu. Często dla celów dowolnej logiki reprezentują one równoważne pojęcia. Nie zawsze , ale często.
jpmc26,
1
@ jpmc26: Myślę, że celem tej odpowiedzi jest stwierdzenie, że kodowanie wytycznych dla c # ma inne znaczenie. Zawsze możesz poprawić kod, gdzie puste i puste są takie same, jeśli chcesz. W niektórych przypadkach możesz zrobić to samo, bez względu na to, czy jest to wartość zerowa czy pusta, ale to nie znaczy, że oznaczają to samo.
Chris,
@Chris I mówię, że wytyczne dotyczące kodowania od osób, które zaprojektowały język, nie mogą być używane jako zamiennik do oceny twojego kodu w kontekście twoich wymagań, używanych bibliotek i innych programistów, z którymi pracujesz. Pomysł, że nigdy nie będą równoważne w kontekście, to idealizm pie-in-the-sky. Mogą nawet nie być równoważne dla ogólnie dostępnych danych, ale mogą być równoważne do generowania wyniku w określonej funkcji, którą piszesz; w takim przypadku musisz wesprzeć oba, ale upewnij się, że generują ten sam wynik.
jpmc26,
Mogę być zdezorientowany tym, co mówisz, ale kiedy wartość pusta i pusta są równoważne do generowania wyniku, nie rozumiem, dlaczego nie używałbyś oddzielnej wartości pustej i jest pustych czeków zamiast poszukiwania metody, która działa jednocześnie, ale nie pozwala na rozróżnienie ich w innych sytuacjach ...
Chris
@Chris Mam na myśli różne konteksty (które często odpowiadają zakresom). Rozważmy na przykład funkcję. nulli pusty może skutkować taką samą wartością zwracaną dla funkcji, ale to nie znaczy, że nulli pusty oznacza dokładnie to samo w zakresie wywoływania.
jpmc26
3

Istnieje wiele odpowiedzi wyjaśniających, dlaczego nulli puste są różne, oraz wystarczająca liczba opinii próbujących wyjaśnić, dlaczego należy je traktować inaczej, czy nie. Jednak pytasz:

Czy się mylę, jako klient tego kodu, oczekując, że np. ((Int []) null) .Każdy () powinien zwrócić wartość false?

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 zasadniczo Count() > 0to, czego oczekujesz od tego fragmentu?

List<int> list = null;
if (list.Count > 0) {}

Lub ogólny:

List<int> list = null;
if (list.Foo()) {}

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ą.
  • Nie każdy język .NET obsługuje metody rozszerzeń.
  • Zawsze możesz zadzwonić Enumerable.Any(null)i tam na pewno się spodziewasz ArgumentNullException. Jest to ta sama metoda i musi być zgodna z - być może - prawie WSZYSTKIM innym w ramach.
  • Dostęp do nullobiektu 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ć.
  • Jest opiniotwórczy , myślisz w jeden sposób, a ja w inny sposób. Ramy powinny być w jak największym stopniu niedoceniane.
  • Jeśli masz szczególny przypadek, musisz być konsekwentny : musisz podejmować coraz bardziej przemyślane decyzje dotyczące wszystkich innych metod rozszerzenia: jeśli Count()wydaje się to łatwa decyzja, Where()nie jest. Co Max()? Zgłasza wyjątek dla listy PUSTEJ, czy nie powinno to również wyrzucić dla nulljednego?

To, co projektanci bibliotek zrobili przed LINQ, polegało na wprowadzeniu jawnych metod, gdy nulljest to prawidłowa wartość (na przykład String.IsNullOrEmpty()), wtedy MUSI być spójne z istniejącą filozofią projektowania . To powiedziawszy, nawet jeśli dość proste do napisania, dwie metody EmptyIfNull()i EmptyOrNull()mogą się przydać.

Adriano Repetti
źródło
1

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.

public bool Any( [NotNull] List bag ) 

Ale Twój łańcuch narzędzi musi to obsługiwać. Co oznacza, że ​​prawdopodobnie nadal będziesz pisać czeki.

candied_orange
źródło
0

Jeśli tak bardzo Ci to przeszkadza, proponuję prostą metodę rozszerzenia.

static public IEnumerable<T> NullToEmpty<T>(this IEnumerable<T> source)
{
    return (source == null) ? Enumerable.Empty<T>() : source;
}

Teraz możesz to zrobić:

List<string> list = null;
var flag = list.NullToEmpty().Any( s => s == "Foo" );

... a flaga zostanie ustawiona na false.

John Wu
źródło
2
Bardziej naturalne jest napisanie returnoświadczenia w następujący sposób:return source ?? Enumerable.Empty<T>();
Jeppe Stig Nielsen
To nie odpowiada na zadane pytanie.
Kenneth K.,
@ KennethK. ale wydaje się, że to rozwiązuje przedstawiony problem.
RQDQ,
0

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 :

Metody rozszerzeń umożliwiają „dodawanie” metod do istniejących typów bez tworzenia nowego typu pochodnego, ponownej kompilacji lub innego modyfikowania oryginalnego typu. Metody rozszerzeń są specjalnym rodzajem metod statycznych, ale są wywoływane tak, jakby były metodami instancji typu rozszerzonego. W przypadku kodu klienta napisanego w C #, F # i Visual Basic nie ma widocznej różnicy między wywołaniem metody rozszerzenia a metodami, które są faktycznie zdefiniowane w typie.

Ogólnie zalecamy oszczędne wdrażanie metod rozszerzeń i tylko wtedy, gdy jest to konieczne. O ile to możliwe, kod klienta, który musi rozszerzyć istniejący typ, powinien to zrobić, tworząc nowy typ pochodzący z istniejącego typu. Aby uzyskać więcej informacji, zobacz Dziedziczenie.

Korzystając z metody rozszerzenia do rozszerzenia typu, którego kodu źródłowego nie można zmienić, istnieje ryzyko, że zmiana w implementacji typu spowoduje uszkodzenie metody rozszerzenia.

Jeśli implementujesz metody rozszerzenia dla danego typu, pamiętaj o następujących punktach:

  • Metoda rozszerzenia nigdy nie zostanie wywołana, jeśli ma taki sam podpis jak metoda zdefiniowana w typie.
  • Metody rozszerzeń są wprowadzane w zakres na poziomie przestrzeni nazw. Na przykład, jeśli masz wiele klas statycznych, które zawierają metody rozszerzeń w pojedynczej nazwanej przestrzeni nazw Extensions, wszystkie one zostaną objęte zakresem using Extensions;dyrektywy.

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.IEnumerablei System.Collections.Generic.IEnumerable<T>, zarówno w swoich zbiorach i foreachpętle. Klasy te zwrócone IEnumeratorprzedmioty, które miał tylko dwie metody Currenti MoveNext, więc dodanie dodatkowych wymaganych metod przykład, takie jak Count, Anyitd 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 warunkach Currenti MoveNextze względną łatwością), wydali go jako metod rozszerzeń, które mogą być stosowane do każdego obecnie istniejącychIEnumerablewystą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 instancji IEnumerablei prawdopodobnie zaprojektowaliby jakiś system zapewniający domyślne implementacje interfejsu tych metod.

TheHansinator
źródło
0

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.

Callum Bradbury
źródło
0

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.

phyrfox
źródło