Якщо служба скидає винятки або повертається, коли для видалення не вказано жодних елементів


12

У мене є фрагмент коду, який можна представити як:

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

Тепер мені цікаво, чи це правильний підхід чи варто кинути виняток. Я можу уникати винятку, тому що повернення було б таким самим, як ітерація над порожньою колекцією, тобто жоден важливий код не виконується, але з іншого боку я, можливо, приховую проблеми десь у коді, бо чому б хто хотів зателефонувати DeleteItemsз nullпараметром? Це може вказувати на те, що в коді є проблема десь ще.

Ця проблема, як правило, у мене з методами в сервісах, тому що більшість з них щось робить і не повертає результат, тому якщо хтось передає недійсну інформацію, то сервісу нічого не робити, тому вона повертається.


2
Це лише моя думка, але я завжди вважав, що має найбільш сенс, що метод повинен викидати виняток, коли він робить щось ненавмисне (виняткове). У вашому випадку я б кинув InvalidOperationException, якби хтось спробував видалити null / 0 елементів.
Фальгантіль


1
@gnat Моє запитання стосується конкретно послуг, через те, як вони використовуються та їх призначення. Цей "дублікат" говорить лише про загальні випадки, і головна відповідь навіть суперечить собі в контексті мого точного методу.
FCin

2
Чи можливо, що нумерація буде порожньою, а не нульовою? Чи поводилися б ви з цим по-іншому?
JAD

13
@Falgantil Я можу побачити, що null заслуговує на виняток, але я вважаю, що недоцільно кидати виняток у порожній список.
Кевін

Відповіді:


58

Це два різні питання.

Чи варто приймати null? Це залежить від вашої загальної політики щодо nullкодової бази. На мою думку, заборона nullскрізь, за винятком випадків, коли це чітко зафіксовано, є дуже хорошою практикою, але ще кращою є практика дотримуватися конвенції, яка вже є у вашій кодовій базі.

Чи варто приймати порожню колекцію? На мою думку: ТАК, абсолютно. Набагато більше зусиль обмежити всіх абонентів не порожніми колекціями, ніж зробити математично правильну річ - навіть якщо це здивує деяких розробників, котрі не впорядковуються концепцією нуля.


9
У будь-якому випадку, якщо ви збираєтеся кинути, то не так багато корисності в киданні, AhaINoticedYouPassingInNullExceptionколи час виконання вже надає вам можливість кинути NullReferenceExceptionдля вас, без того, щоб ви взагалі писали якийсь код. Існує деяка утиліта (наприклад, ваш інструмент покриття коду підкаже, чи є у вас одиничний тест на проходження нуля в першому випадку, але не в останньому), але жоден виняток не слід намагатися / спіймати під час кожного дзвінка до служби, тому що зазвичай це помилка програміста.
Стів Джессоп

2
... вам слід негайно зафіксувати винятки, підняті внаслідок ескізних параметрів, якщо ви знаєте, що те, що ви передаєте, є нульовим у певному очікуваному випадку, і ви активно хочете, щоб функція, яку ви викликаєте, перевірила її для вас. Як каже Кіліан у відповіді, ваша загальна політика може полягати в тому, щоб забороняти нуль скрізь, коли це прямо не дозволено. Тоді ви б не потрапили NullReferenceExceptionні AhaINoticedYouPassingInNullExceptionде-небудь, окрім як на високому рівні код відновлення аварій. І цей обробник винятків, мабуть, повинен створити звіт про помилку :-)
Стів Джессоп

2
@FCin: ваш дизайн інтерфейсу повинен відповідати тому, що насправді потребує користувачів у сервісі. Отже, якщо кожен абонент збирається постаратися спробувати це, то або цей метод, або метод зручності поруч із ним повинен перевіряти і ігнорувати нуль, тому що це вимагає ваша громадськість. Однак, як каже Кіліан, зазвичай краще розцінювати поширення нулів як помилку програмування, а не намагатися продовжувати на кожному сайті виклику. З цього приводу, що, якщо служба, за якою називається цей метод, null- чи містять контролери котлову планку, щоб ловити та продовжувати і в цьому випадку?
Стів Джессоп

2
... якщо так, то, схоже, база коду послідовно розглядає відсутні компоненти, як очікуване умова, з яким слід працювати на низькому рівні та продовжувати. Ви можете обґрунтовано запитати: "чия відповідальність за надання послуги та їх кількість, щоб ця операція могла продовжуватися?". Якщо відповідь - "хтось", то ваша функція - перевірка передумов , а результатом невдалої передумови зазвичай буде виняток, що потрапляє на високому рівні, а не на кожен дзвінок. Якщо необхідні матеріали для операції справді необов’язкові, то ваша функція обробляє очікуваний випадок використання .
Стів Джессоп

2
Очевидно, що викидання aver ArgumentNullExceptionє кращим за те, щоб дозволити інтер'єрному коду кидати NullReferenceException- чисто дивлячись на повідомлення про виняток, очевидно, що це помилка введення, а не помилка логіки в тілі методу на сайті викидання, а забезпечення раннього виходу означає, що ви, швидше за все, залишили інші дані у дійсному стані, а не частково вимкнено пізніше несподівану нуль.
Міраль

9

Нульове значення

Як вже говорив @KilianFoth, дотримуйтесь загальної політики. Якщо це nullстосується порожнього списку як "скорочення", зробіть це так.

Якщо у вас немає послідовної політики щодо nullцінностей, я рекомендую наступну:

nullслід зарезервувати для представлення ситуацій, які не можуть бути виражені типом "нормальний", наприклад, використовуючи nullдля представлення "я не знаю". І це вдалий вибір, оскільки кожен, хто намагається недбало використовувати цю цінність, отримає виняток, що є правильним.

Використання nullяк скорочення для порожнього списку не є таким чином, оскільки вже є ідеальне представлення, оскільки це список з нульовими елементами. І це технічно поганий вибір, оскільки він змушує кожну частину вашого коду, що займається списками, перевіряти чи справжню стенографію null.

Порожній список

Для DeleteItems()методу проходження порожнього списку фактично означає нічого не робити. Я дозволю це як аргумент, не кидаючи винятку, просто повернувшись швидко.

Звичайно, абонент може спочатку перевірити наявність нульових елементів і пропустити DeleteItems()виклик у такому випадку. Якщо ми говоримо про веб-API, то з міркувань ефективності абонент повинен насправді зробити це, щоб уникнути непотрібного трафіку та зворотних затримок. Але я не думаю, що ваш API повинен це застосовувати.


1

Вкиньте виняток і обробіть нулі в коді виклику.

Як правило дизайну намагайтеся уникати нульових значень параметрів. Це загалом зменшить NullPointerExceptions, оскільки нулі дійсно будуть винятком.

Окрім цього, подивіться на решту коду. Якщо це звичайний зразок у вашому проекті, то залишайтеся послідовними.


Я в середині "формую" структуру моїх служб, тому може знадобитися деякий рефакторинг, щоб застосувати недійсний вхід, але не дуже багато. Але я погоджуюся з вашою думкою, що нулі стануть винятком, як тільки я почну кидати замість того, щоб їх приховувати.
FCin

0

Взагалі кажучи, кидання винятків повинно бути зарезервоване для виняткових ситуацій, тобто якщо код не має розумного курсу дій у поточному контексті.

Ви можете застосувати цей мислительний процес до цієї ситуації. Зважаючи на те, що це публічний клас з публічним методом, у вас є публічний API, а теоретично немає контролю над тим, що йому передається.

У вашого коду немає контексту щодо того, що викликає його (як це не повинно), і ви не можете нічого розумно зробити з нульовим значенням. Це, безумовно, було б кандидатом на вступ ArgumentNullException.


"якщо код не має розумного курсу дій у поточному контексті". Але, на мій погляд, він має розумний хід дій, який полягає у поверненні нікчемності. Це робиться точно так само, як і при передачі порожньої колекції, тому якщо я дозволяю пусту колекцію, то чому б я не дозволив null.
FCin

1
@FCin Через порожню колекцію ви знаєте, що вона порожня. З nullвами немає колекції. Якщо викликовий код повинен / очікується, що він надасть колекцію методу, щось не так, тож слід кинути. Єдина причина поводитись з нульовою точкою, що і з порожньою колекцією, це якщо ви могли обґрунтовано очікувати, що викликовий код створить нульові колекції. Як зазначали інші відповіді, більшість випадків щось nullє аномалією. Якщо ви ставитесь до них так само, як до порожніх колекцій, ви потенційно ігноруєте проблему в іншому місці коду.
JAD

@FCin: також якщо ви вважаєте nullпорожнім, це специфічно для цього методу. Десь ще в тій самій базі коду, у вас може бути параметр IEnumerableабо IContainerпараметр, який виконує якесь фільтрування, nullможе означати "немає фільтра" (дозволити все), тоді як порожній фільтр означає, що нічого не дозволяють. А в іншому місці nullможе явно означати "я не знаю". Отже, зважаючи на те, nullщо не завжди означає скрізь одне й те саме, корисно взагалі не вигадувати для нього жодного сенсу, якщо цей сенс не потрібен чи принаймні комусь корисний.
Стів Джессоп

0

Здається, це питання не стосується винятків, а nullє вагомим аргументом.

Отже, перш за все, ви повинні вирішити, чи nullдозволене значення для вашого аргументу цього методу. Якщо це так, то вам не потрібно винятку. Якщо це не так, то вам потрібно виключення.

Хочете ви дозволити nullчи ні, це дискусійне питання, про що свідчать багато і багато звернень Google щодо цього. Значить, ви не отримаєте чіткої відповіді, і це дещо відповідає думці та традиціям у тому місці, де ви працюєте.

Ще одна суперечка - чи має бути функція бібліотеки справді суворою щодо таких речей, або якомога поблажливішою, доки вона не намагається «виправити» помилкові параметри. Порівняйте це зі світом мережевих протоколів, передачі пошти тощо (які є інтерфейсними контрактами, подібно до методів програмування). Зазвичай політика полягає в тому, що відправник повинен максимально суворо відповідати протоколу, тоді як одержувач повинен вийти зі свого шляху, щоб мати можливість працювати з тим, що відбувається.

Тож вам доведеться вирішити: чи справді завдання бібліотечного методу застосовувати досить велику політику, наприклад nullобробку? Особливо, якщо вашою бібліотекою користуються й інші люди (які можуть мати різні правила).

Я, мабуть null, помиляюсь зі стороною дозволу значень, визначення та документування їх семантики (тобто, null = empty arrayу цьому випадку), а не кидаю виняток, якщо тільки 99% вашого іншого подібного коду ("бібліотечний" код стилю) не робить іншим способом 'круглий.


1
"Чи справді завдання бібліотечного методу застосовувати досить велику політику, наприклад обробку нуля?" - внизу цієї лінії мислення лежать твердження та режими налагодження, які дозволяють вам допомогти в застосуванні політики, а не насправді виконувати її, якщо це саме ви хочете зробити. Отже, якщо ви збираєтесь проковтнути nullпринцип ліберальності в тому, що ви приймаєте, то реєстрація чи навіть кидання буде корисною людям, чий намір полягає в тому, щоб бути суворим у тому, що вони передають, але які зіпсували і свій код, і їхні одиничні тести настільки, наскільки вони nullвсе-таки проходять .
Стів Джессоп

@SteveJessop, я насправді не знаю, чи ти сперечаєшся з духом моєї відповіді, чи ти продовжуєш її узгоджувати. Сказавши це, я не пропоную просто проковтнути null, але заявити про це відкрито в договорі методу, що він прийнятний (чи ні, залежно від того, яке рішення висуває ОП), а потім питання, чи кидати виняток (чи ні) вирішує сам.
AnoE
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.