У меня есть метод, где вся логика выполняется внутри цикла foreach, который перебирает параметр метода:
public IEnumerable<TransformedNode> TransformNodes(IEnumerable<Node> nodes)
{
foreach(var node in nodes)
{
// yadda yadda yadda
yield return transformedNode;
}
}
В этом случае отправка пустой коллекции приводит к пустой коллекции, но мне интересно, если это неразумно.
Моя логика здесь заключается в том, что если кто-то вызывает этот метод, то он намеревается передать данные и будет передавать пустую коллекцию моему методу только в ошибочных обстоятельствах.
Должен ли я поймать это поведение и выдать для него исключение, или лучше возвращать пустую коллекцию?
c#
exceptions
collections
parameters
Ник Уделл
источник
источник
null
но нет, если она пустая.Ответы:
Служебные методы не должны выбрасывать пустые коллекции. Ваши клиенты API будут ненавидеть вас за это.
Коллекция может быть пустой; концептуально «коллекция, которая не должна быть пустой» - намного сложнее работать.
Преобразование пустой коллекции имеет очевидный результат: пустая коллекция. (Вы можете даже сохранить немного мусора, возвращая сам параметр.)
Существует много обстоятельств, при которых модуль поддерживает списки материалов, которые могут быть или не быть уже заполнены чем-то. Необходимость проверять пустоту перед каждым призывом
transform
раздражает и может превратить простой, элегантный алгоритм в безобразный беспорядок.Служебные методы всегда должны стремиться быть либеральными по своим вкладам и консервативными по своим результатам.
Ради всего святого, по всем этим причинам правильно обращайтесь с пустой коллекцией. Нет ничего более раздражающего, чем вспомогательный модуль, который думает, что знает, что вы хотите лучше, чем вы.
источник
null
в пустую коллекцию. Функции с неявными ограничениями - это боль.Populate1(input); output1 = TransformNodes(input); Populate2(input); output2 = TransformNodes(input);
если Populate1 оставил коллекцию пустой и вы вернули ее для первого вызова TransformNodes, output1 и input будут одной и той же коллекцией, и при вызове Populaten2, если он поместит узлы в коллекцию, вы получите вторую набор ввода в output2.Я вижу два важных вопроса, которые определяют ответ на этот вопрос:
1. Значимое возвращение
По сути, если вы можете вернуть что-то осмысленное, то не выбрасывайте исключение. Позвольте звонящему разобраться с результатом. Так что если ваша функция ...
В целом, мой уклон FP говорит мне «вернуть что-то значимое», и в этом случае значение null может иметь действительное значение.
2. Стиль
Ваш общий код (или код проекта или код команды) поддерживает функциональный стиль? Если нет, то исключения будут ожидаться и рассматриваться. Если да, то рассмотрите возможность возврата типа параметра . С типом параметра вы либо возвращаете значимый ответ, либо None / Nothing . В моем третьем примере, приведенном выше, ничего не будет хорошим ответом в стиле FP. Тот факт, что функция возвращает тип Option, сигнализирует вызывающей стороне явно, а не значимый ответ, может быть невозможным, и вызывающая сторона должна быть готова к этому. Я чувствую, что это дает звонящему больше возможностей (если вы простите за каламбур).
F #, где все прохладные .Net дети делают такие вещи , но C # делает поддержку этого стиля.
ТЛ; др
Сохраняйте исключения для непредвиденных ошибок в своем собственном пути к коду, а не полностью предсказуемым (и законным) вводом от кого-либо еще.
источник
Как всегда, это зависит.
Имеет ли значение, что коллекция пуста?
Большая часть кода обработки коллекции, вероятно, сказала бы «нет»; В коллекции может быть любое количество элементов, включая ноль.
Теперь, если у вас есть какая-то коллекция, в которой «недопустимо» не иметь элементов, то это новое требование, и вы должны решить, что с этим делать.
Возьмите некоторую логику тестирования из мира Базы данных: тестируйте ноль предметов, один предмет и два предмета. Это обслуживает наиболее важные случаи (вымывание любых плохо сформированных внутренних или декартовых условий соединения).
источник
java.util.Collection
а пользовательскимcom.foo.util.NonEmptyCollection
классом, который может последовательно сохранять этот инвариант и не позволять вам перейти в недопустимое состояние для начала.Принимая во внимание хороший дизайн, примите как можно больше изменений в ваших входах. Исключения должны создаваться только тогда, когда (недопустимый ввод данных ИЛИ непредвиденные ошибки происходят во время обработки) И в результате программа не может продолжаться предсказуемым образом .
В этом случае следует ожидать, что будет представлена пустая коллекция, и ваш код должен обработать ее (что он уже делает). Было бы нарушением всего, что было бы хорошо, если бы ваш код выдал исключение здесь. Это было бы сродни умножению 0 на 0 в математике. Это избыточно, но абсолютно необходимо, чтобы оно работало так, как оно работает.
Теперь перейдем к аргументу нулевой коллекции. В этом случае нулевая коллекция является ошибкой программирования: программист забыл присвоить переменную. Это тот случай, когда может быть выдано исключение, потому что вы не можете осмысленно обработать это в выводе, и попытка сделать это приведет к неожиданному поведению. Это было бы сродни делению на ноль в математике - это совершенно бессмысленно.
источник
Правильное решение гораздо труднее увидеть, когда вы просто смотрите на свою функцию изолированно. Рассматривайте свою функцию как часть более крупной проблемы . Одно из возможных решений этого примера выглядит следующим образом (в Scala):
Сначала вы разбиваете строку на нецифровые символы, отфильтровываете пустые строки, преобразуете строки в целые числа, а затем фильтруете, чтобы сохранить только четырехзначные числа. Ваша функция может быть
map (_.toInt)
в процессе.Этот код довольно прост, потому что каждый этап в конвейере обрабатывает только пустую строку или пустую коллекцию. Если вы поместите пустую строку в начале, вы получите пустой список в конце. Вам не нужно останавливаться и проверять наличие
null
или исключение после каждого звонка.Конечно, это предполагает, что пустой список вывода не имеет более одного значения. Если вам необходимо различать пустой вывод, вызванный пустым вводом, и вывод, вызванный самим преобразованием, это полностью меняет дело.
источник
Этот вопрос на самом деле об исключениях. Если вы посмотрите на это так и проигнорируете пустую коллекцию как деталь реализации, ответ будет простым:
1) Метод должен генерировать исключение, когда он не может продолжить: либо не может выполнить назначенную задачу, либо возвращает соответствующее значение.
2) Метод должен перехватить исключение, когда он может продолжить работу, несмотря на ошибку.
Таким образом, ваш вспомогательный метод не должен быть «полезным» и генерировать исключение, если он не может выполнить свою работу с пустой коллекцией. Пусть звонящий определит, можно ли обработать результаты.
Возвращает ли он пустую коллекцию или ноль, немного сложнее, но не намного: обнуляемых коллекций следует избегать, если это возможно. Цель обнуляемой коллекции будет указывать (как в SQL), что у вас нет информации - например, коллекция детей может быть нулевой, если вы не знаете, есть ли у кого-то такая информация, но вы этого не делаете. знаю, что они этого не делают. Но если это важно по какой-то причине, возможно, стоит отслеживать дополнительную переменную.
источник
Метод назван
TransformNodes
. В случае пустой коллекции в качестве входных данных, возвращение пустой коллекции является естественным и интуитивно понятным, и имеет префект математический смысл.Если бы метод был назван
Max
и предназначен для возврата максимального элемента, то было бы естественноNoSuchElementException
создать пустую коллекцию, так как максимум ничего не имеет математического смысла.Если метод был назван
JoinSqlColumnNames
и предназначен для возврата строки, в которой элементы объединяются запятой для использования в запросах SQL, то имеет смысл добавитьIllegalArgumentException
пустую коллекцию, так как вызывающий в конечном итоге получит ошибку SQL в любом случае, если он использует строка в запросе SQL напрямую без дальнейших проверок, и вместо проверки на возвращаемую пустую строку он действительно должен был вместо этого проверить пустую коллекцию.источник
Max
из ничего часто бывает отрицательная бесконечность.Давайте вернемся назад и используем другой пример, который вычисляет среднее арифметическое для массива значений.
Если входной массив пуст (или равен нулю), можете ли вы разумно выполнить запрос вызывающей стороны? Нет. Какие у тебя варианты? Ну, вы могли бы:
Я говорю, дайте им ошибку, если они дали вам неправильный ввод и запрос не может быть завершен. Я имею в виду серьезную ошибку с первого дня, чтобы они поняли требования вашей программы. В конце концов, ваша функция не в состоянии ответить. Если операция может завершиться неудачей (например, скопировать файл), тогда ваш API должен выдать им ошибку, с которой они могут иметь дело.
Таким образом, это может определить, как ваша библиотека обрабатывает искаженные запросы и запросы, которые могут быть неудачными.
Для вашего кода очень важно быть последовательным в том, как он обрабатывает эти классы ошибок.
Следующая категория - решить, как ваша библиотека обрабатывает бессмысленные запросы. Возвращаясь к примеру подобного Вашим - давайте использовать функцию , которая определяет , существует ли файл в пути:
bool FileExistsAtPath(String)
. Если клиент передает пустую строку, как вы справляетесь с этим сценарием? Как насчет пустого или нулевого массива, переданного вvoid SaveDocuments(Array<Document>)
? Выберите свою библиотеку / кодовую базу и будьте последовательны, Я считаю, что в этих случаях ошибки, и запрещаю клиентам делать бессмысленные запросы, помечая их как ошибки (через утверждение). Некоторые люди будут сильно сопротивляться этой идее / действию. Я считаю это обнаружение ошибок очень полезным. Это очень хорошо для обнаружения проблем в программах - с хорошей локализацией для программы-нарушителя. Программы намного понятнее и правильнее (учитывайте эволюцию вашей кодовой базы) и не записывают циклы внутри функций, которые ничего не делают. Таким образом, код становится меньше / чище, и проверки обычно выталкиваются в места, где может возникнуть проблема.источник
if (!FileExists(path)) { ...create it!!!... }
ошибок - многие из которых были бы обнаружены до фиксации, если бы линии корректности не были размыты.Как правило, стандартная функция должна быть в состоянии принять самый широкий список входных данных и дать отзыв о нем, есть много примеров, когда программисты используют функции способами, которые дизайнер не планировал, с учетом этого я считаю, что функция должен иметь возможность принимать не только пустые коллекции, но и широкий диапазон типов ввода и изящно возвращать обратную связь, даже если это объект ошибки от какой-либо операции, выполненной на входе ...
источник
writePaycheckToEmployee
не следует принимать отрицательное число в качестве входных данных ... но если «обратная связь» означает «делает что-то, что может произойти дальше», тогда да, каждая функция будет делать то, что она делает дальше.