Mam fragment kodu, który można przedstawić jako:
public class ItemService {
public void DeleteItems(IEnumerable<Item> items)
{
// Save us from possible NullReferenceException below.
if(items == null)
return;
foreach(var item in items)
{
// For the purpose of this example, lets say I have to iterate over them.
// Go to database and delete them.
}
}
}
Teraz zastanawiam się, czy jest to właściwe podejście, czy powinienem rzucić wyjątek. Mogę uniknąć wyjątku, ponieważ zwracanie byłoby tym samym, co iteracja po pustej kolekcji, co oznacza, że i tak żaden ważny kod nie jest wykonywany, ale z drugiej strony prawdopodobnie ukryję gdzieś problemy w kodzie, ponieważ dlaczego ktoś miałby dzwonić DeleteItems
z null
parametrem? Może to oznaczać, że w kodzie występuje problem.
Jest to problem, który zwykle mam z metodami w usługach, ponieważ większość z nich coś robi i nie zwraca wyniku, więc jeśli ktoś przekaże nieprawidłowe informacje, usługa nie ma nic do zrobienia, więc zwraca.
c#
exceptions
error-handling
FCin
źródło
źródło
Odpowiedzi:
To są dwa różne pytania.
Czy powinieneś zaakceptować
null
? To zależy od twojej ogólnej zasadynull
w bazie kodu. Moim zdaniem banowanienull
wszędzie, z wyjątkiem przypadków, w których jest to wyraźnie udokumentowane, jest bardzo dobrą praktyką, ale jeszcze lepszą praktyką jest przestrzeganie konwencji, którą posiada już twoja baza kodu.Czy powinieneś zaakceptować pustą kolekcję? Moim zdaniem: TAK, absolutnie. Znacznie większy wysiłek stanowi ograniczenie wszystkich osób dzwoniących do niepustych kolekcji niż zrobienie matematycznie właściwej rzeczy - nawet jeśli zaskakuje to niektórych programistów, którzy nie mają pojęcia o zero.
źródło
AhaINoticedYouPassingInNullException
gdy środowisko wykonawcze już zapewnia ci możliwość rzucaniaNullReferenceException
za ciebie, bez pisania żadnego kodu. Istnieje pewne narzędzie (np. Twoje narzędzie do pokrycia kodu powie ci, czy masz test jednostkowy na przekazanie wartości null w pierwszym przypadku, ale nie w drugim), ale żaden wyjątek nie powinien być próbowany / wychwytywany przy każdym wywołaniu usługi, ponieważ zwykle jest to błąd programisty.NullReferenceException
aniAhaINoticedYouPassingInNullException
nigdzie indziej, niż na wysokim poziomie kodu odzyskiwania po awarii. I ten program obsługi wyjątków powinien prawdopodobnie utworzyć raport o błędzie :-)null
- czy kontrolery zawierają płytę kotłową do przechwycenia i kontynuowania również w tym przypadku?ArgumentNullException
pozwala na wyrzucenie kodu wewnętrznegoNullReferenceException
- po prostu patrząc na komunikat wyjątku jest oczywiste, że jest to błąd wejściowy, a nie błąd logiczny w treści metody w miejscu rzucania, a zapewnienie wczesnego wyjścia oznacza, że bardziej prawdopodobne jest, że pozostawisz inne dane w poprawnym stanie, a nie częściowo zmutowane później z nieoczekiwanego null.Wartość zerowa
Jak już powiedział @KilianFoth, trzymaj się swojej ogólnej polityki. Jeśli ma to być traktowane
null
jako „skrót” dla pustej listy, zrób to w ten sposób.Jeśli nie masz spójnych zasad dotyczących
null
wartości, polecam następującą:null
powinny być zarezerwowane do reprezentowania sytuacji, których nie można wyrazić typem „normalnym”, np. używanienull
do reprezentowania „Nie wiem”. I to dobry wybór, ponieważ każdy, kto spróbuje niedbale wykorzystać tę wartość, otrzyma wyjątek, co jest słuszne.Użycie
null
skrótu do pustej listy nie kwalifikuje się w ten sposób, ponieważ istnieje już idealna reprezentacja, będąc listą z zerowymi elementami. Jest to technicznie zły wybór, ponieważ zmusza każdą część kodu zajmującą się listami do sprawdzenia poprawności skrótunull
.Pusta lista
W przypadku
DeleteItems()
metody przekazanie pustej listy skutecznie oznacza nic nie robienie. Pozwoliłbym na to jako argument, nie rzucając wyjątku, po prostu wracając szybko.Oczywiście, osoba dzwoniąca może najpierw sprawdzić zero elementów i
DeleteItems()
w takim przypadku pominąć połączenie. Jeśli mówimy o internetowym interfejsie API, ze względów wydajności osoba dzwoniąca powinna to zrobić, aby uniknąć niepotrzebnego ruchu i opóźnień w obie strony. Ale nie sądzę, że Twój interfejs API powinien to wymuszać.źródło
Zgłaszaj wyjątek i obsługuj wartości zerowe w kodzie wywołującym.
Z reguły należy unikać wartości null jako wartości parametrów. Zmniejszy to ogólnie NullPointerExceptions, ponieważ wartości null będą naprawdę wyjątkiem.
Poza tym spójrz na resztę swojego kodu. Jeśli jest to powszechny wzór w twoim projekcie, zachowaj spójność.
źródło
Ogólnie rzecz biorąc, zgłaszanie wyjątków powinno być zarezerwowane na wyjątkowe sytuacje, tj. Jeśli kod nie ma rozsądnego sposobu działania w obecnym kontekście.
Możesz zastosować ten proces myślowy do tej sytuacji. Biorąc pod uwagę, że jest to klasa publiczna z publiczną metodą, masz publiczny interfejs API i teoretycznie nie masz kontroli nad tym, co zostanie mu przekazane.
Twój kod nie ma kontekstu na temat tego, co go nazywa (jak nie powinien), i nic nie możesz zrobić z wartością zerową. Z pewnością byłby to kandydat na
ArgumentNullException
.źródło
null
.null
tobą nie masz kolekcji. Jeśli kod wywołujący ma / powinien dostarczyć kolekcję do metody, coś jest nie tak, więc powinieneś rzucić. Jedynym powodem traktowania wartości zerowej tak samo jak pustej kolekcji jest uzasadnione oczekiwanie, że kod wywołujący wygeneruje kolekcje zerowe. Jak zauważyły inne odpowiedzi, w większości przypadków cośnull
jest anomalią. Jeśli traktujesz je tak samo jak puste kolekcje, potencjalnie ignorujesz problem w innym miejscu kodu.null
pusty, jest to kontekstowe dla tej metody. Gdzieś indziej w tej samej bazie kodu, możesz mieć parametrIEnumerable
lubIContainer
, który dokonuje pewnego rodzaju filtrowania,null
może oznaczać „brak filtra” (zezwalaj na wszystko), podczas gdy pusty filtr oznacza, że nic nie zezwala. A w innym miejscunull
może wyraźnie oznaczać „nie wiem”. Biorąc pod uwagę, żenull
nie zawsze oznacza to wszędzie to samo, dobrym pomysłem jest, aby w ogóle nie wymyślać żadnego znaczenia, chyba że to znaczenie jest konieczne lub przynajmniej przydatne dla kogoś.To pytanie wydaje się nie dotyczyć wyjątków, ale
null
być ważnym argumentem.Przede wszystkim musisz zdecydować, czy
null
argument jest dozwolony dla argumentu tej metody. Jeśli tak, to nie potrzebujesz wyjątku. Jeśli tak nie jest, potrzebujesz wyjątku.To, czy chcesz na to zezwolić
null
, czy nie, jest dyskusyjne, o czym świadczy wiele hitów Google na ten temat. Oznacza to, że nie dostaniesz jasnej odpowiedzi i zależy to w pewnym stopniu od opinii i tradycji w miejscu pracy.Inną kwestią sporną jest to, czy funkcja biblioteczna powinna być naprawdę surowa w stosunku do takich rzeczy, czy tak łagodna, jak to możliwe, o ile nie próbuje ona „naprawić” błędnych parametrów. Porównaj to ze światem protokołów sieciowych, przesyłania poczty itp. (Które są umowami interfejsowymi, podobnie jak metody programowania). Tam zwykle obowiązuje zasada, że nadawca powinien możliwie ściśle przestrzegać protokołu, a odbiorca powinien starać się pracować z tym, co nadchodzi.
Musisz więc zdecydować: czy naprawdę zadaniem metody bibliotecznej jest egzekwowanie dość dużych zasad, takich jak
null
obsługa? Zwłaszcza jeśli Twoja biblioteka jest używana również przez inne osoby (które mogą mieć inne zasady).Prawdopodobnie popełniłbym błąd po stronie dopuszczania
null
wartości, definiowania i dokumentowania ich semantyki (tj.null = empty array
W tym przypadku), i nie rzucałbym wyjątku, chyba że 99% twojego innego podobnego kodu (kodu w stylu „biblioteki”) robi to w drugą stronę 'okrągły.źródło
null
zasadę bycia liberalnym w tym, co akceptujesz, wtedy logowanie lub nawet rzucanie byłoby pomocne dla ludzi, którzy zamierzają być surowi w tym, co przekazują, ale którzy pomieszali zarówno swój kod, jak i ich testy jednostkowe w zakresie, w jakim inull
tak zdają .null
, ale deklaruję otwarcie w umowie metody, że jest to dopuszczalne (lub nie, w zależności od rozwiązania, jakie pojawi się OP), a następnie pytanie, czy zgłosić wyjątek (lub nie) rozwiązuje się.