To rodzaj ogólnego pytania (ale używam C #), jaki jest najlepszy sposób (najlepsza praktyka), czy zwracasz wartość zerową lub pustą kolekcję dla metody, która ma kolekcję jako typ zwracany?
To rodzaj ogólnego pytania (ale używam C #), jaki jest najlepszy sposób (najlepsza praktyka), czy zwracasz wartość zerową lub pustą kolekcję dla metody, która ma kolekcję jako typ zwracany?
Odpowiedzi:
Pusta kolekcja. Zawsze.
To jest do bani:
if(myInstance.CollectionProperty != null)
{
foreach(var item in myInstance.CollectionProperty)
/* arrgh */
}
Uznaje się za najlepszą praktykę, aby NIGDY nie zwracać null
przy zwracaniu kolekcji lub listy. ZAWSZE zwracaj pusty wyliczalny / zbiór. Zapobiega wspomnianym bzdurom i zapobiega popychaniu samochodu przez współpracowników i użytkowników twoich zajęć.
Mówiąc o nieruchomościach, zawsze ustaw swoją właściwość raz i zapomnij o niej
public List<Foo> Foos {public get; private set;}
public Bar() { Foos = new List<Foo>(); }
W .NET 4.6.1 można to bardzo mocno skondensować:
public List<Foo> Foos { get; } = new List<Foo>();
Mówiąc o metodach, które zwracają wyliczenia, możesz łatwo zwrócić pusty wyliczenie zamiast null
...
public IEnumerable<Foo> GetMyFoos()
{
return InnerGetFoos() ?? Enumerable.Empty<Foo>();
}
Używanie Enumerable.Empty<T>()
może być postrzegane jako bardziej wydajne niż zwracanie, na przykład nowej pustej kolekcji lub tablicy.
IEnumerable
czy ICollection
nie. W każdym razie, jeśli wybierzesz coś w rodzaju, ICollection
oni również zwrócą null
... Chciałbym, aby zwrócili pustą kolekcję, ale natknąłem się na ich powrót null
, więc pomyślałem, że po prostu wspomnę o tym tutaj. Powiedziałbym, że domyślna kolekcja wyliczalnych jest pusta, a nie zerowa. Nie wiedziałem, że to taki delikatny temat.
Z wytycznych projektowania ramowego, wydanie drugie (str. 256):
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ę.
Oto kolejny interesujący artykuł o korzyściach płynących z nie zwracania wartości zerowych (próbowałem znaleźć coś na blogu Brada Abrama, a on link do tego artykułu).
Edytuj - ponieważ Eric Lippert skomentował już oryginalne pytanie, chciałbym również zamieścić link do jego doskonałego artykułu .
Zależy od umowy i konkretnego przypadku . Ogólnie najlepiej jest zwracać puste kolekcje , ale czasami ( rzadko ):
null
może oznaczać coś bardziej konkretnego;null
.Niektóre konkretne przykłady:
null
oznaczałoby to brak elementu, a pusta kolekcja spowodowałaby nadmiar (i być może niepoprawny)<collection />
Jest jeszcze jeden punkt, o którym jeszcze nie wspomniano. Rozważ następujący kod:
public static IEnumerable<string> GetFavoriteEmoSongs()
{
yield break;
}
Język C # zwróci pusty moduł wyliczający podczas wywoływania tej metody. Dlatego, aby zachować spójność z projektem języka (a tym samym oczekiwaniami programisty), należy zwrócić pustą kolekcję.
Pusty jest o wiele bardziej przyjazny dla konsumenta.
Istnieje jasna metoda tworzenia pustego wyliczenia:
Enumerable.Empty<Element>()
Wydaje mi się, że powinieneś zwrócić wartość, która jest semantycznie poprawna w kontekście, cokolwiek by to nie było. Reguła, która mówi „zawsze zwracaj pustą kolekcję” wydaje mi się trochę uproszczona.
Załóżmy, że w systemie szpitalnym mamy funkcję, która ma zwrócić listę wszystkich poprzednich hospitalizacji z ostatnich 5 lat. Jeśli klient nie był w szpitalu, warto zwrócić pustą listę. Ale co, jeśli klient pozostawił tę część formularza wstępu pustą? Potrzebujemy innej wartości, aby odróżnić „pustą listę” od „brak odpowiedzi” lub „nie wiem”. Możemy rzucić wyjątek, ale niekoniecznie jest to warunek błędu i niekoniecznie wyprowadza nas z normalnego przepływu programu.
Często byłem sfrustrowany systemami, które nie potrafią rozróżnić od zera do braku odpowiedzi. Wielokrotnie system prosił mnie o podanie pewnej liczby, wpisuję zero i pojawia się komunikat o błędzie z informacją, że muszę wprowadzić wartość w tym polu. Właśnie tak zrobiłem: wpisałem zero! Ale nie przyjmie zera, ponieważ nie może odróżnić go od braku odpowiedzi.
Odpowiedz Saundersowi:
Tak, zakładam, że istnieje różnica między „Osoba nie odpowiedziała na pytanie” a „Odpowiedź była zerowa”. To był punkt ostatniego akapitu mojej odpowiedzi. Wiele programów nie jest w stanie odróżnić „nie wiem” od pustego lub zerowego, co wydaje mi się potencjalnie poważną wadą. Na przykład kupowałem dom jakiś rok temu. Poszedłem na stronę z nieruchomościami i było tam wiele domów z ceną wywoławczą 0 USD. Brzmiało dla mnie całkiem dobrze: oddają te domy za darmo! Ale jestem pewien, że smutną rzeczywistością było to, że po prostu nie podali ceny. W takim przypadku możesz powiedzieć: „Cóż, oczywiście zero oznacza, że nie podali ceny - nikt nie oddaje domu za darmo”. Ale na stronie wymieniono również średnie ceny sprzedaży i sprzedaży domów w różnych miastach. Nie mogę przestać się zastanawiać, czy średnia nie zawierała zer, dając w ten sposób nieprawidłowo niską średnią dla niektórych miejsc. tj. jaka jest średnia 100 000 USD; 120 000 USD; i „nie wiem”? Technicznie odpowiedź brzmi „nie wiem”. To, co prawdopodobnie naprawdę chcemy zobaczyć, to 110 000 USD. Ale prawdopodobnie dostaniemy 73 333 USD, co byłoby całkowicie błędne. A co, gdybyśmy mieli ten problem w witrynie, w której użytkownicy mogą zamawiać online? (Jest to mało prawdopodobne w przypadku nieruchomości, ale jestem pewien, że widziałeś to już w przypadku wielu innych produktów.) Czy naprawdę chcielibyśmy, aby „cena jeszcze nieokreślona” była interpretowana jako „darmowa”? dając w ten sposób nieprawidłowo niską średnią dla niektórych miejsc. tj. jaka jest średnia 100 000 USD; 120 000 USD; i „nie wiem”? Technicznie odpowiedź brzmi „nie wiem”. To, co prawdopodobnie naprawdę chcemy zobaczyć, to 110 000 USD. Ale prawdopodobnie dostaniemy 73 333 USD, co byłoby całkowicie błędne. A co, gdybyśmy mieli ten problem w witrynie, w której użytkownicy mogą zamawiać online? (Jest to mało prawdopodobne w przypadku nieruchomości, ale jestem pewien, że widziałeś to już w przypadku wielu innych produktów.) Czy naprawdę chcielibyśmy, aby „cena jeszcze nieokreślona” była interpretowana jako „darmowa”? dając w ten sposób nieprawidłowo niską średnią dla niektórych miejsc. tj. jaka jest średnia 100 000 USD; 120 000 USD; i „nie wiem”? Technicznie odpowiedź brzmi „nie wiem”. To, co prawdopodobnie naprawdę chcemy zobaczyć, to 110 000 USD. Ale prawdopodobnie dostaniemy 73 333 USD, co byłoby całkowicie błędne. A co, gdybyśmy mieli ten problem w witrynie, w której użytkownicy mogą zamawiać online? (Jest to mało prawdopodobne w przypadku nieruchomości, ale jestem pewien, że widziałeś to już w przypadku wielu innych produktów.) Czy naprawdę chcielibyśmy, aby „cena jeszcze nieokreślona” była interpretowana jako „darmowa”? co byłoby całkowicie błędne. A co, gdybyśmy mieli ten problem w witrynie, w której użytkownicy mogą zamawiać online? (Jest to mało prawdopodobne w przypadku nieruchomości, ale jestem pewien, że widziałeś to już w przypadku wielu innych produktów.) Czy naprawdę chcielibyśmy, aby „cena jeszcze nieokreślona” była interpretowana jako „darmowa”? co byłoby całkowicie błędne. A co, gdybyśmy mieli ten problem w witrynie, w której użytkownicy mogą zamawiać online? (Jest to mało prawdopodobne w przypadku nieruchomości, ale jestem pewien, że widziałeś to już w przypadku wielu innych produktów.) Czy naprawdę chcielibyśmy, aby „cena jeszcze nieokreślona” była interpretowana jako „darmowa”?
RE ma dwie oddzielne funkcje, „czy jest jakaś?” i „jeśli tak, co to jest?” Tak, na pewno możesz to zrobić, ale dlaczego miałbyś chcieć? Teraz program wywołujący musi wykonać dwa połączenia zamiast jednego. Co się stanie, jeśli programista nie wywoła „any”? i przechodzi od razu do „co to jest?” ? Czy program zwróci błędne zero? Rzuć wyjątek? Zwrócić niezdefiniowaną wartość? Tworzy więcej kodu, więcej pracy i więcej potencjalnych błędów.
Jedyną korzyścią, jaką widzę, jest to, że pozwala ci przestrzegać arbitralnej reguły. Czy jest jakaś zaleta tej reguły, która sprawia, że warto kłopotać się jej przestrzeganiem? Jeśli nie, to po co?
Odpowiedz na Jammycakes:
Zastanów się, jak wyglądałby rzeczywisty kod. Wiem, że pytanie brzmiało C #, ale przepraszam, jeśli piszę w Javie. Moje C # nie jest bardzo ostre i zasada jest taka sama.
Ze znakiem zerowym:
HospList list=patient.getHospitalizationList(patientId);
if (list==null)
{
// ... handle missing list ...
}
else
{
for (HospEntry entry : list)
// ... do whatever ...
}
Z osobną funkcją:
if (patient.hasHospitalizationList(patientId))
{
// ... handle missing list ...
}
else
{
HospList=patient.getHospitalizationList(patientId))
for (HospEntry entry : list)
// ... do whatever ...
}
W rzeczywistości jest to wiersz lub dwa mniej kodu ze znakiem zerowym, więc nie stanowi większego obciążenia dla osoby dzwoniącej, jest mniej.
Nie rozumiem, jak to powoduje problem z SUCHEM. To nie tak, że musimy wykonać połączenie dwukrotnie. Gdybyśmy zawsze chcieli zrobić to samo, gdy lista nie istnieje, być może moglibyśmy przesunąć obsługę do funkcji get-list, zamiast wywoływania wywoływania, a więc umieszczenie kodu w wywołującym byłoby naruszeniem DRY. Ale prawie na pewno nie chcemy zawsze robić tego samego. W funkcjach, w których musimy mieć listę do przetworzenia, brakująca lista jest błędem, który może zatrzymać przetwarzanie. Ale na ekranie edycji z pewnością nie chcemy przerwać przetwarzania, jeśli jeszcze nie wprowadzili danych: chcemy pozwolić im na wprowadzanie danych. Tak więc obsługa „brak listy” musi odbywać się na poziomie osoby dzwoniącej w taki czy inny sposób. A czy robimy to z zerowym znakiem powrotu czy osobną funkcją, nie ma znaczenia dla większej zasady.
Jasne, jeśli program wywołujący nie sprawdzi wartości null, program może zawieść z wyjątkiem wyjątku wskaźnika zerowego. Ale jeśli istnieje osobna funkcja „ma jakąkolwiek”, a osoba dzwoniąca nie wywołuje tej funkcji, ale ślepo wywołuje funkcję „pobierz listę”, to co się dzieje? Jeśli zgłasza wyjątek lub w inny sposób zawiedzie, cóż, to prawie tak samo, jak by się stało, gdyby zwrócił wartość null i nie sprawdził go. Jeśli zwraca pustą listę, to po prostu źle. Nie rozróżniasz między „Mam listę z zerowymi elementami” a „Nie mam listy”. To jak zwracanie zera ceny, gdy użytkownik nie podał żadnej ceny: to po prostu źle.
Nie rozumiem, w jaki sposób dołączenie dodatkowego atrybutu do kolekcji pomaga. Dzwoniący nadal musi to sprawdzić. Dlaczego to jest lepsze niż sprawdzanie wartości null? Ponownie absolutną najgorszą rzeczą, jaka może się zdarzyć, jest zapomnienie przez programistę o sprawdzeniu i podanie niepoprawnych wyników.
Funkcja, która zwraca null, nie jest zaskoczeniem, jeśli programista zna pojęcie wartości null oznaczające „nie ma wartości”, o czym myślę, że każdy kompetentny programista powinien słyszeć, niezależnie od tego, czy uważa, że to dobry pomysł, czy nie. Myślę, że posiadanie oddzielnej funkcji jest raczej problemem „zaskoczenia”. Jeśli programista nie zna interfejsu API, gdy uruchomi test bez danych, szybko odkryje, że czasami odzyskuje wartość zerową. Ale jak odkryłby istnienie innej funkcji, gdyby nie przyszło mu do głowy, że taka funkcja może istnieć, i sprawdza dokumentację, a dokumentacja jest kompletna i zrozumiała? Wolałbym mieć jedną funkcję, która zawsze daje mi znaczącą odpowiedź, niż dwie funkcje, które muszę znać i pamiętać, aby wywołać oba.
Jeśli pusta kolekcja ma sens semantyczny, wolę to zwrócić. Zwracanie pustej kolekcji do GetMessagesInMyInbox()
komunikowania „naprawdę nie masz żadnych wiadomości w skrzynce odbiorczej”, podczas gdy zwracanie null
może być przydatne, aby poinformować, że dostępne są niewystarczające dane, aby powiedzieć, jak powinna wyglądać zwrócona lista.
null
wartość na pewno nie wydaje się rozsądna, myślałem o niej bardziej ogólnie. Wyjątki są również świetne do informowania o tym, że coś poszło nie tak, ale jeśli „niewystarczające dane”, o których mowa, są doskonale oczekiwane, to rzucenie wyjątku byłoby złym projektem. Myślę raczej o scenariuszu, w którym jest to całkowicie możliwe i nie ma w ogóle błędu, że metoda czasami nie jest w stanie obliczyć odpowiedzi.
Zwracanie wartości null może być bardziej wydajne, ponieważ nie jest tworzony nowy obiekt. Często wymagałoby to jednak null
sprawdzenia (lub obsługi wyjątków).
Semantycznie null
i pusta lista nie oznaczają tego samego. Różnice są subtelne i w niektórych przypadkach jeden wybór może być lepszy niż drugi.
Niezależnie od wyboru udokumentuj go, aby uniknąć zamieszania.
Można argumentować, że uzasadnienie Null Object Pattern jest podobne do argumentu przemawiającego za zwróceniem pustej kolekcji.
Zależy od sytuacji. Jeśli jest to szczególny przypadek, zwróć null. Jeśli funkcja zwraca akurat pustą kolekcję, to oczywiście zwracanie jest w porządku. Jednak zwracanie pustej kolekcji jako specjalnego przypadku z powodu niepoprawnych parametrów lub z innych powodów NIE jest dobrym pomysłem, ponieważ maskuje specjalny przypadek.
Właściwie w tym przypadku zazwyczaj wolę rzucić wyjątek, aby upewnić się, że NAPRAWDĘ nie jest ignorowany :)
Mówienie, że sprawia, że kod jest bardziej niezawodny (przez zwrócenie pustej kolekcji), ponieważ nie muszą one obsługiwać warunku zerowego, jest złe, ponieważ po prostu maskuje problem, który powinien zostać rozwiązany przez kod wywołujący.
Twierdziłbym, że null
to nie to samo, co pusta kolekcja i powinieneś wybrać, która najlepiej reprezentuje to, co oddajesz. W większości przypadków null
jest to nic (oprócz SQL). Pusta kolekcja to coś, choć coś pustego.
Jeśli musisz wybrać jedno lub drugie, powiedziałbym, że powinieneś raczej wybierać pustą kolekcję niż zerową. Ale są chwile, kiedy pusta kolekcja nie jest tym samym, co wartość pusta.
Zawsze myśl na korzyść swoich klientów (którzy używają interfejsu API):
Zwracanie „null” bardzo często powoduje problemy z klientami niepoprawnie obsługującymi kontrole null, co powoduje wyjątek NullPointerException podczas działania. Widziałem przypadki, w których takie brakujące sprawdzanie wartości zerowej wymuszało priorytetowy problem z produkcją (klient używał foreach (...) dla wartości zerowej). Podczas testowania problem nie wystąpił, ponieważ operowane dane były nieco inne.
Chciałbym tu wyjaśnić, podając odpowiedni przykład.
Rozważ przypadek tutaj ..
int totalValue = MySession.ListCustomerAccounts()
.FindAll(ac => ac.AccountHead.AccountHeadID
== accountHead.AccountHeadID)
.Sum(account => account.AccountValue);
Tutaj rozważ funkcje, których używam ..
1. ListCustomerAccounts() // User Defined
2. FindAll() // Pre-defined Library Function
Mogę łatwo używać ListCustomerAccount
i FindAll
zamiast.,
int totalValue = 0;
List<CustomerAccounts> custAccounts = ListCustomerAccounts();
if(custAccounts !=null ){
List<CustomerAccounts> custAccountsFiltered =
custAccounts.FindAll(ac => ac.AccountHead.AccountHeadID
== accountHead.AccountHeadID );
if(custAccountsFiltered != null)
totalValue = custAccountsFiltered.Sum(account =>
account.AccountValue).ToString();
}
UWAGA: Ponieważ AccountValue nie jest null
, funkcja Sum () nie zwróci null
., Dlatego mogę z niej korzystać bezpośrednio.
Zwracanie pustej kolekcji jest lepsze w większości przypadków.
Powodem tego jest wygoda implementacji dzwoniącego, spójna umowa i łatwiejsza implementacja.
Jeśli metoda zwraca wartość null w celu wskazania pustego wyniku, program wywołujący musi oprócz implementacji wyliczenia zaimplementować adapter sprawdzania wartości null. Ten kod jest następnie duplikowany w różnych obiektach wywołujących, więc dlaczego nie umieścić tego adaptera w metodzie, aby można go było ponownie użyć.
Prawidłowe użycie wartości null dla IEnumerable może wskazywać na brak wyniku lub błąd operacji, ale w takim przypadku należy rozważyć inne techniki, takie jak zgłoszenie wyjątku.
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
namespace StackOverflow.EmptyCollectionUsageTests.Tests
{
/// <summary>
/// Demonstrates different approaches for empty collection results.
/// </summary>
class Container
{
/// <summary>
/// Elements list.
/// Not initialized to an empty collection here for the purpose of demonstration of usage along with <see cref="Populate"/> method.
/// </summary>
private List<Element> elements;
/// <summary>
/// Gets elements if any
/// </summary>
/// <returns>Returns elements or empty collection.</returns>
public IEnumerable<Element> GetElements()
{
return elements ?? Enumerable.Empty<Element>();
}
/// <summary>
/// Initializes the container with some results, if any.
/// </summary>
public void Populate()
{
elements = new List<Element>();
}
/// <summary>
/// Gets elements. Throws <see cref="InvalidOperationException"/> if not populated.
/// </summary>
/// <returns>Returns <see cref="IEnumerable{T}"/> of <see cref="Element"/>.</returns>
public IEnumerable<Element> GetElementsStrict()
{
if (elements == null)
{
throw new InvalidOperationException("You must call Populate before calling this method.");
}
return elements;
}
/// <summary>
/// Gets elements, empty collection or nothing.
/// </summary>
/// <returns>Returns <see cref="IEnumerable{T}"/> of <see cref="Element"/>, with zero or more elements, or null in some cases.</returns>
public IEnumerable<Element> GetElementsInconvenientCareless()
{
return elements;
}
/// <summary>
/// Gets elements or nothing.
/// </summary>
/// <returns>Returns <see cref="IEnumerable{T}"/> of <see cref="Element"/>, with elements, or null in case of empty collection.</returns>
/// <remarks>We are lucky that elements is a List, otherwise enumeration would be needed.</remarks>
public IEnumerable<Element> GetElementsInconvenientCarefull()
{
if (elements == null || elements.Count == 0)
{
return null;
}
return elements;
}
}
class Element
{
}
/// <summary>
/// http://stackoverflow.com/questions/1969993/is-it-better-to-return-null-or-empty-collection/
/// </summary>
class EmptyCollectionTests
{
private Container container;
[SetUp]
public void SetUp()
{
container = new Container();
}
/// <summary>
/// Forgiving contract - caller does not have to implement null check in addition to enumeration.
/// </summary>
[Test]
public void UseGetElements()
{
Assert.AreEqual(0, container.GetElements().Count());
}
/// <summary>
/// Forget to <see cref="Container.Populate"/> and use strict method.
/// </summary>
[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void WrongUseOfStrictContract()
{
container.GetElementsStrict().Count();
}
/// <summary>
/// Call <see cref="Container.Populate"/> and use strict method.
/// </summary>
[Test]
public void CorrectUsaOfStrictContract()
{
container.Populate();
Assert.AreEqual(0, container.GetElementsStrict().Count());
}
/// <summary>
/// Inconvenient contract - needs a local variable.
/// </summary>
[Test]
public void CarefulUseOfCarelessMethod()
{
var elements = container.GetElementsInconvenientCareless();
Assert.AreEqual(0, elements == null ? 0 : elements.Count());
}
/// <summary>
/// Inconvenient contract - duplicate call in order to use in context of an single expression.
/// </summary>
[Test]
public void LameCarefulUseOfCarelessMethod()
{
Assert.AreEqual(0, container.GetElementsInconvenientCareless() == null ? 0 : container.GetElementsInconvenientCareless().Count());
}
[Test]
public void LuckyCarelessUseOfCarelessMethod()
{
// INIT
var praySomeoneCalledPopulateBefore = (Action)(()=>container.Populate());
praySomeoneCalledPopulateBefore();
// ACT //ASSERT
Assert.AreEqual(0, container.GetElementsInconvenientCareless().Count());
}
/// <summary>
/// Excercise <see cref="ArgumentNullException"/> because of null passed to <see cref="Enumerable.Count{TSource}(System.Collections.Generic.IEnumerable{TSource})"/>
/// </summary>
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void UnfortunateCarelessUseOfCarelessMethod()
{
Assert.AreEqual(0, container.GetElementsInconvenientCareless().Count());
}
/// <summary>
/// Demonstrates the client code flow relying on returning null for empty collection.
/// Exception is due to <see cref="Enumerable.First{TSource}(System.Collections.Generic.IEnumerable{TSource})"/> on an empty collection.
/// </summary>
[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void UnfortunateEducatedUseOfCarelessMethod()
{
container.Populate();
var elements = container.GetElementsInconvenientCareless();
if (elements == null)
{
Assert.Inconclusive();
}
Assert.IsNotNull(elements.First());
}
/// <summary>
/// Demonstrates the client code is bloated a bit, to compensate for implementation 'cleverness'.
/// We can throw away the nullness result, because we don't know if the operation succeeded or not anyway.
/// We are unfortunate to create a new instance of an empty collection.
/// We might have already had one inside the implementation,
/// but it have been discarded then in an effort to return null for empty collection.
/// </summary>
[Test]
public void EducatedUseOfCarefullMethod()
{
Assert.AreEqual(0, (container.GetElementsInconvenientCarefull() ?? Enumerable.Empty<Element>()).Count());
}
}
}
Nazywam to moim błędem za miliard dolarów… W tym czasie projektowałem pierwszy kompleksowy system typów dla odniesień w języku zorientowanym obiektowo. Moim celem było upewnienie się, że każde użycie referencji powinno być całkowicie bezpieczne, a sprawdzanie wykonywane automatycznie przez kompilator. Ale nie mogłem oprzeć się pokusie wprowadzenia zerowego odniesienia, po prostu dlatego, że było to tak łatwe do wdrożenia. Doprowadziło to do niezliczonych błędów, słabych punktów i awarii systemu, które prawdopodobnie spowodowały miliard dolarów bólu i szkód w ciągu ostatnich czterdziestu lat. - Tony Hoare, wynalazca ALGOL W.
Zobacz tutajnull
ogólnie misterną burzę gówna . Nie zgadzam się ze stwierdzeniem, które undefined
jest inne null
, ale nadal warto je przeczytać. I wyjaśnia, dlaczego null
w ogóle powinieneś unikać, a nie tylko w przypadku, o który prosiłeś. Istotą jest to, że null
w każdym języku jest to szczególny przypadek. Musisz myśleć o tym null
jako o wyjątku. undefined
różni się tym, że kod zajmujący się niezdefiniowanym zachowaniem jest w większości przypadków tylko błędem. C i większość innych języków ma również niezdefiniowane zachowanie, ale większość z nich nie ma identyfikatora tego w języku.
Z punktu widzenia zarządzania złożonością, głównym celem inżynierii oprogramowania, chcemy uniknąć propagowania niepotrzebnej cykliczności złożoności wśród klientów interfejsu API. Zwracanie wartości zerowej do klienta jest jak zwracanie cyklicznego kosztu złożoności innej gałęzi kodu.
(Odpowiada to obciążeniu związanemu z testowaniem jednostkowym. Konieczne byłoby napisanie testu dla zerowej skrzynki zwrotnej, oprócz pustej skrzynki zwrotnej kolekcji).