Какова лучшая практика при возврате данных из функций. Лучше вернуть нулевой или пустой объект? И почему один должен делать один над другим?
Учти это:
public UserEntity GetUserById(Guid userId)
{
//Imagine some code here to access database.....
//Check if data was returned and return a null if none found
if (!DataExists)
return null;
//Should I be doing this here instead?
//return new UserEntity();
else
return existingUserEntity;
}
Давайте представим, что в этой программе будут действительные случаи, когда в базе данных не будет никакой пользовательской информации с этим GUID. Я предположил бы, что это не было бы уместно бросить исключение в этом случае ?? Также у меня сложилось впечатление, что обработка исключений может снизить производительность.
if (!DataExists)
.Ответы:
Возврат нулевого значения обычно является лучшей идеей, если вы намереваетесь указать, что данные недоступны.
Пустой объект подразумевает, что данные были возвращены, тогда как возвращение нуля ясно указывает на то, что ничего не было возвращено.
Кроме того, возвращение нулевого значения приведет к нулевому исключению, если вы попытаетесь получить доступ к элементам в объекте, что может быть полезно для выделения ошибочного кода - попытка получить доступ к элементу ничего не имеет смысла. Доступ к членам пустого объекта не потерпит неудачу, а значит ошибки могут остаться незамеченными.
источник
bool GetUserById(Guid userId, out UserEntity result)
Я бы предпочел нулевое» возвращаемое значение, и это не так экстремально, как создание исключения. Это позволяет красивый, какnull
бесплатный кодif(GetUserById(x,u)) { ... }
.Это зависит от того, что имеет смысл для вашего случая.
Имеет ли смысл возвращать ноль, например, «такого пользователя нет»?
Или имеет смысл создать пользователя по умолчанию? Это имеет больше смысла, когда вы можете с уверенностью предположить, что если пользователь НЕ существует, вызывающий код предполагает его существование, когда его об этом просят.
Или имеет смысл генерировать исключение (в виде «FileNotFound»), если вызывающий код требует пользователя с неверным идентификатором?
Однако - с точки зрения разделения интересов / SRP, первые два являются более правильными. И технически первое является наиболее правильным (но только на волосок) - GetUserById должен отвечать только за одну вещь - за получение пользователя. Обработка собственного случая «пользователь не существует» путем возврата чего-либо еще может быть нарушением SRP. Разделение на другую проверку -
bool DoesUserExist(id)
было бы целесообразно, если вы решите создать исключение.Основываясь на подробных комментариях ниже : если это вопрос разработки уровня API, этот метод может быть аналогом «OpenFile» или «ReadEntireFile». Мы «открываем» пользователя из какого-либо хранилища и гидратируем объект из полученных данных. Исключение может быть уместным в этом случае. Это может быть не так, но это может быть.
Все подходы являются приемлемыми - это просто зависит от более широкого контекста API / приложения.
источник
Лично я использую NULL. Это дает понять, что нет данных для возврата. Но бывают случаи, когда нулевой объект может быть полезен.
источник
Если ваш тип возвращаемого значения - массив, тогда верните пустой массив, иначе верните ноль.
источник
null
. Это позволяет использовать его вforeach
операторах и запросах linq, не беспокоясь оNullReferenceException
.Вы должны бросить исключение (только), если конкретный контракт нарушен.
В вашем конкретном примере, запрашивая UserEntity на основе известного идентификатора, это будет зависеть от того, является ли пропущенный (удаленный) пользователь ожидаемым случаем. Если это так, тогда вернитесь,
null
но если это не ожидаемый случай, выведите исключение.Обратите внимание, что если бы функция была вызвана,
UserEntity GetUserByName(string name)
она, вероятно, не сгенерирует, а вернет ноль. В обоих случаях возвращение пустого UserEntity было бы бесполезным.Для строк, массивов и коллекций ситуация обычно отличается. Я помню некоторые инструкции из MS, которые методы должны принимать
null
как «пустой» список, но возвращать коллекции нулевой длины, а неnull
. То же самое для строк. Обратите внимание, что вы можете объявить пустые массивы:int[] arr = new int[0];
источник
.Wher(p => p.Lastname == "qwerty")
должен вернуть пустую коллекцию, а неnull
.Это бизнес-вопрос, зависящий от того, является ли существование пользователя с определенным идентификатором Guid ожидаемым нормальным сценарием использования этой функции, или это аномалия, которая помешает приложению успешно завершить любую функцию, которую этот метод предоставляет пользователю Возражать...
Если это «исключение», то отсутствие пользователя с таким идентификатором не позволит приложению успешно завершить выполнение любой выполняемой им функции (например, мы создаем счет-фактуру для клиента, которому мы отправили продукт ... ), то эта ситуация должна вызвать исключение ArgumentException (или какое-либо другое пользовательское исключение).
Если с пропавшим пользователем все в порядке (один из возможных нормальных результатов вызова этой функции), тогда верните нуль ....
РЕДАКТИРОВАТЬ: (чтобы обратиться к комментарию от Адама в другом ответе)
Если приложение содержит несколько бизнес-процессов, один или несколько из которых требуют, чтобы пользователь успешно завершил свою работу, и один или несколько из которых могут успешно завершиться без участия пользователя, то исключение следует выдвинуть дальше вверх по стеку вызовов, ближе к месту бизнес-процессы, которые требуют от пользователя, вызывают этот поток выполнения. Методы между этим методом и той точкой (где генерируется исключение) должны просто сообщать, что пользователь не существует (null, boolean, что угодно - это деталь реализации).
Но если бы все процессы в приложении требовали пользователя, я бы все равно бросил исключение в этом методе ...
источник
Лично я бы возвратил ноль, потому что именно так я ожидаю, что слой DAL / Repository будет действовать.
Если он не существует, не возвращайте ничего, что может быть истолковано как успешное извлечение объекта,
null
здесь прекрасно работает.Самое главное - быть последовательным на уровне DAL / Repos, чтобы не запутаться в том, как его использовать.
источник
Я склонен
return null
если идентификатор объекта не существует, когда заранее неизвестно, должен ли он существовать.throw
если идентификатор объекта не существует, когда он должен существовать.Я различаю эти два сценария с этими тремя типами методов. Первый:
Во-вторых:
Третий:
источник
out
нетref
Еще один подход включает передачу объекта обратного вызова или делегата, который будет работать со значением. Если значение не найдено, обратный вызов не вызывается.
Это хорошо работает, когда вы хотите избежать нулевых проверок во всем коде, и если не найти значение, это не ошибка. Вы также можете предоставить обратный вызов, если объекты не найдены, если вам нужна специальная обработка.
Тот же подход с использованием одного объекта может выглядеть следующим образом:
С точки зрения дизайна мне действительно нравится этот подход, но он имеет недостаток, заключающийся в том, что сайт вызова становится более объемным в языках, которые не поддерживают функции первого класса.
источник
Мы используем CSLA.NET и придерживаемся мнения, что неудачная выборка данных должна возвращать «пустой» объект. Это на самом деле довольно раздражает, так как требует соглашения о проверке,
obj.IsNew
а неobj == null
.Как упоминалось в предыдущем постере, нулевые возвращаемые значения немедленно приведут к сбою кода, что уменьшит вероятность скрытых проблем, вызванных пустыми объектами.
Лично я думаю, что
null
это более элегантно.Это очень распространенный случай, и я удивлен, что люди здесь удивляются этому: в любом веб-приложении данные часто выбираются с помощью параметра querystring, который, очевидно, может быть искажен, поэтому требуется, чтобы разработчик обрабатывал случаи «not found». ».
Вы можете справиться с этим путем:
... но это дополнительный вызов в базу данных каждый раз, что может быть проблемой на страницах с большим трафиком. В то время как:
... требуется только один звонок.
источник
Я предпочитаю
null
, так как он совместим с оператором null-coalescing (??
).источник
Я бы сказал, вернуть пустой вместо пустого объекта.
Но конкретный экземпляр, который вы упомянули здесь, вы ищете пользователя по идентификатору пользователя, который является своего рода ключом к этому пользователю, в этом случае я бы, вероятно, захотел бы вызвать исключение, если экземпляр пользователя не найден ,
Это правило, которому я обычно следую:
источник
Это будет зависеть от контекста, но я обычно возвращаю ноль, если я ищу один конкретный объект (как в вашем примере), и возвращаю пустую коллекцию, если я ищу набор объектов, но их нет.
Если вы допустили ошибку в своем коде и возвращение нулевого значения приводит к исключениям нулевого указателя, то чем раньше вы поймете это, тем лучше. Если вы возвращаете пустой объект, его первоначальное использование может работать, но вы можете получить ошибки позже.
источник
Лучшие в этом случае возвращают «ноль», если такого пользователя нет. Также сделайте ваш метод статичным.
Редактировать:
Обычно такие методы являются членами некоторого класса «Пользователь» и не имеют доступа к его членам экземпляра. В этом случае метод должен быть статическим, в противном случае необходимо создать экземпляр «User», а затем вызвать метод GetUserById, который будет возвращать другой экземпляр «User». Согласитесь, это сбивает с толку. Но если метод GetUserById является членом некоторого класса «DatabaseFactory» - не проблема оставить его в качестве члена экземпляра.
источник
Я лично возвращаю экземпляр объекта по умолчанию. Причина в том, что я ожидаю, что метод вернет ноль ко многим или ноль к единице (в зависимости от цели метода). Единственная причина, по которой при таком подходе это будет состояние ошибки любого рода, заключается в том, что метод не возвратил ни одного объекта (объектов) и всегда ожидался (с точки зрения единичного или единичного возврата).
Что касается предположения, что это вопрос бизнес-сферы - я просто не вижу его с той стороны уравнения. Нормализация типов возвращаемых данных является допустимым вопросом архитектуры приложения. По крайней мере, он подлежит стандартизации в практике кодирования. Я сомневаюсь, что есть бизнес-пользователь, который собирается сказать «в сценарии X просто дать ему ноль».
источник
В наших Business Objects у нас есть 2 основных метода Get:
Для простоты в контексте, или вы задаете вопрос, они будут:
Первый метод используется при получении конкретных объектов, второй метод используется специально при добавлении или редактировании объектов на веб-страницах.
Это позволяет нам иметь лучшее из обоих миров в контексте, где они используются.
источник
Я французский студент IT, так что извините за мой плохой английский. В наших классах нам говорят, что такой метод никогда не должен возвращать ни ноль, ни пустой объект. Пользователь этого метода должен сначала проверить, что объект, который он ищет, существует, прежде чем пытаться его получить.
Используя Java, нас просят добавить
assert exists(object) : "You shouldn't try to access an object that doesn't exist";
в начале любого метода, который мог бы возвратить нуль, чтобы выразить «предварительное условие» (я не знаю, что это за слово по-английски).ИМО, это действительно не просто использовать, но это то, что я использую, ожидая чего-то лучшего.
источник
Если случай, когда пользователь не найден, встречается достаточно часто, и вы хотите по-разному с этим справляться в зависимости от обстоятельств (иногда выбрасывая исключение, иногда подставляя пустого пользователя), вы также можете использовать что-то близкое к типу F #
Option
или Haskell.Maybe
, который явно отделяет регистр «без значения» от «нашел что-то!». Код доступа к базе данных может выглядеть так:И использовать так:
К сожалению, каждый, кажется, бросает подобный себе тип.
источник
Я обычно возвращаю ноль. Он обеспечивает быстрый и простой механизм, позволяющий обнаружить, что что-то напортачило, без исключений и использования сотен проб и ловушек повсюду.
источник
Для типов коллекции я бы возвратил Пустую коллекцию, для всех других типов я предпочитаю использовать шаблоны NullObject для возврата объекта, который реализует тот же интерфейс, что и интерфейс возвращаемого типа. для получения подробной информации о шаблоне проверить текст ссылки
Используя шаблон NullObject, это будет:
{// Представьте себе некоторый код для доступа к базе данных .....
}
источник
Чтобы выразить то, что другие сказали более уместно ...
Исключения для исключительных обстоятельств
Если этот метод является чистым уровнем доступа к данным, я бы сказал, что с учетом некоторого параметра, который включается в оператор выбора, можно ожидать, что я не смогу найти никаких строк, из которых можно построить объект, и поэтому возвращение нуля будет приемлемым, так как логика доступа к данным
С другой стороны, если бы я ожидал, что мой параметр будет отражать первичный ключ, и я должен получить только одну строку назад, если я получу более одного обратно, я бы сгенерировал исключение. 0 - нормально, чтобы вернуть ноль, 2 - нет.
Теперь, если бы у меня был какой-то код входа в систему, который был проверен у провайдера LDAP, а затем проверен у БД, чтобы получить более подробную информацию, и я ожидал, что они должны быть синхронизированы всегда, тогда я мог бы выбросить исключение. Как говорили другие, это бизнес-правила.
Теперь я скажу, что это общее правило. Есть моменты, когда вы можете сломать это. Тем не менее, мой опыт и эксперименты с C # (много чего) и Java (кое-что) научили меня, что гораздо эффективнее работать с исключениями, чем обрабатывать предсказуемые проблемы с помощью условной логики. Я говорю о мелодии на 2 или 3 порядка дороже в некоторых случаях. Итак, если возможно, что ваш код может оказаться в цикле, я бы посоветовал вернуть null и протестировать его.
источник
Прости мой псевдо-php / код.
Я думаю, что это действительно зависит от предполагаемого использования результата.
Если вы намереваетесь отредактировать / изменить возвращаемое значение и сохранить его, верните пустой объект. Таким образом, вы можете использовать ту же функцию для заполнения данных о новом или существующем объекте.
Скажем, у меня есть функция, которая берет первичный ключ и массив данных, заполняет строку данными, а затем сохраняет полученную запись в БД. Так как я собираюсь заполнить объект моими данными в любом случае, может быть огромным преимуществом получить пустой объект от получателя. Таким образом, я могу выполнять идентичные операции в любом случае. Вы используете результат функции получения независимо от того, что.
Пример:
Здесь мы видим, что одна и та же серия операций управляет всеми записями этого типа.
Однако, если конечная цель возвращаемого значения - прочитать и что-то сделать с данными, то я бы возвратил ноль. Таким образом, я могу очень быстро определить, не были ли возвращены данные, и отобразить соответствующее сообщение для пользователя.
Обычно я отлавливаю исключения в своей функции, которая извлекает данные (чтобы я мог регистрировать сообщения об ошибках и т. Д.), А затем возвращает ноль прямо из перехвата. Как правило, для конечного пользователя не имеет значения, в чем заключается проблема, поэтому я считаю, что лучше всего инкапсулировать мою регистрацию / обработку ошибок непосредственно в функции, которая получает данные. Если вы поддерживаете общую кодовую базу в любой крупной компании, это особенно полезно, потому что вы можете принудительно регистрировать и обрабатывать ошибки даже для самого ленивого программиста.
Пример:
Это мое общее правило. Пока это хорошо работает.
источник
Интересный вопрос, и я думаю, что нет «правильного» ответа, так как он всегда зависит от ответственности вашего кода. Ваш метод знает, являются ли найденные данные проблемой или нет? В большинстве случаев ответом является «нет», и поэтому возвращение нулевого значения и разрешение вызывающей стороне его ситуации является идеальным.
Возможно, хороший подход, чтобы отличить методы броска от методов с нулевым возвратом, состоит в том, чтобы найти соглашение в вашей команде: методы, которые говорят, что они «получают» что-то, должны генерировать исключение, если нечего получить. Методы, которые могут возвращать null, могут быть названы по-другому, возможно, вместо этого «Find ...».
источник
Если возвращаемый объект является чем-то, что может быть повторено, я бы возвратил пустой объект, так что мне не нужно сначала проверять на ноль.
Пример:
источник
Мне нравится не возвращать нуль из любого метода, но вместо этого использовать функциональный тип Option. Методы, которые не могут вернуть результат, возвращают пустой параметр, а не ноль.
Кроме того, такие методы, которые не могут вернуть результат, должны указывать это через свое имя. Обычно я помещаю Try или TryGet или TryFind в начало имени метода, чтобы указать, что он может вернуть пустой результат (например, TryFindCustomer, TryLoadFile и т. Д.).
Это позволяет вызывающему применять различные методы, как сбор конвейерные (см Мартина Фаулера Коллекция Pipeline ) на результате.
Вот еще один пример, где возвращаемый Option вместо null используется для уменьшения сложности кода: Как уменьшить цикломатическую сложность: функциональный тип Option
источник
Больше мяса для измельчения: допустим, мой DAL возвращает NULL для GetPersonByID, как советуют некоторые. Что должен делать мой (довольно тонкий) BLL, если он получает NULL? Передать этот NULL и позволить конечному потребителю беспокоиться об этом (в данном случае страница ASP.Net)? Как насчет того, чтобы BLL выдал исключение?
BLL может использоваться ASP.Net и Win App, или другой библиотекой классов - я думаю, что было бы несправедливо ожидать, что конечный потребитель «знает», что метод GetPersonByID возвращает ноль (если, конечно, не используются нулевые типы) ).
Мое мнение (для чего это стоит) состоит в том, что мой DAL возвращает NULL, если ничего не найдено. Для НЕКОТОРЫХ ОБЪЕКТОВ это нормально - это может быть список 0: много вещей, так что отсутствие каких-либо вещей - это хорошо (например, список любимых книг). В этом случае мой BLL возвращает пустой список. Для большинства отдельных объектов (например, пользователь, учетная запись, счет-фактура), если у меня их нет, то это определенно проблема, и это является дорогостоящим исключением. Тем не менее, видя, что поиск пользователя по уникальному идентификатору, который был ранее задан приложением, всегда должен возвращать пользователя, исключение является «правильным» исключением, поскольку оно является исключительным. Конечный потребитель BLL (ASP.Net, f'rinstance) только когда-либо ожидает, что все будет просто так, поэтому вместо упаковки каждого вызова GetPersonByID в блок try-catch будет использоваться обработчик необработанных исключений.
Если в моем подходе есть явная проблема, пожалуйста, дайте мне знать, поскольку я всегда стремлюсь учиться. Как говорили другие авторы, исключения - это дорогостоящие вещи, и подход «проверка в первую очередь» хорош, но исключения должны быть именно такими - исключительными.
Мне нравится этот пост, много хороших предложений для сценариев "все зависит" :-)
источник
Я думаю, что функции не должны возвращать ноль, для здоровья вашей кодовой базы. Я могу придумать несколько причин:
Будет большое количество пунктов охраны, рассматривающих нулевую ссылку
if (f() != null)
.Что это
null
, принятый ответ или проблема? Является ли null допустимым состоянием для определенного объекта? (представьте, что вы клиент для кода). Я имею в виду, что все ссылочные типы могут быть нулевыми, но должны ли они?Имея
null
торчать почти всегда будет давать несколько неожиданных исключений NullRef время от времени , как ваш код базы растет.Есть несколько решений
tester-doer pattern
или реализацияoption type
из функционального программирования.источник
Я озадачен количеством ответов (по всей сети), в которых говорится, что вам нужны два метода: метод IsItThere () и метод GetItForMe (), и это приводит к состоянию гонки. Что не так с функцией, которая возвращает null, присваивая ее переменной и проверяя переменную на Null все в одном тесте? Мой прежний код на C был приправлен
if (NULL! = (variable = function (arguments ...))) {
Таким образом, вы получаете значение (или ноль) в переменной и результат сразу. Эта идиома была забыта? Зачем?
источник
Я согласен с большинством сообщений здесь, которые имеют тенденцию к
null
.Я считаю, что генерация пустого объекта с ненулевыми свойствами может привести к ошибкам. Например, объект со
int ID
свойством будет иметь начальное значениеID = 0
, которое является полностью допустимым значением. Если при некоторых обстоятельствах этот объект будет сохранен в базе данных, это будет плохо.Для чего-либо с итератором я бы всегда использовал пустую коллекцию. Что-то вроде
это запах кода на мой взгляд. Свойства коллекции не должны быть нулевыми, никогда.
Краевой случай есть
String
. Многие люди говорят, чтоString.IsNullOrEmpty
это на самом деле не нужно, но вы не всегда можете различить пустую строку и нулевую. Кроме того, некоторые системы баз данных (Oracle) вообще не различают их (''
хранятся какDBNULL
), поэтому вы вынуждены обращаться с ними одинаково. Причина этого заключается в том, что большинство строковых значений поступают либо из пользовательского ввода, либо из внешних систем, в то время как ни текстовые поля, ни большинство форматов обмена не имеют разных представлений для''
иnull
. Таким образом, даже если пользователь хочет удалить значение, он не может сделать ничего, кроме очистки элемента управления вводом. Кроме того, различиеnvarchar
полей Nullable и Nullable базы данных более чем сомнительно, если ваша СУБД не оракул - обязательное поле, которое позволяет''
странно, ваш пользовательский интерфейс никогда не допустит этого, поэтому ваши ограничения не отображаются. Так что ответ здесь, на мой взгляд, обрабатывать их одинаково, всегда.Относительно вашего вопроса об исключениях и производительности: если вы генерируете исключение, которое вы не можете полностью обработать в логике вашей программы, вы должны в какой-то момент прервать то, что делает ваша программа, и попросить пользователя повторить то, что он только что сделал. В этом случае снижение производительности a
catch
является наименьшим из ваших беспокойств - необходимость спросить пользователя - это слон в комнате (что означает повторную визуализацию всего пользовательского интерфейса или отправку некоторого HTML-кода через Интернет). Поэтому, если вы не следуете анти-шаблону « Программный поток с исключениями », не беспокойтесь, просто бросьте один, если это имеет смысл. Даже в пограничных случаях, таких как «Исключение при валидации», производительность на самом деле не является проблемой, так как в любом случае вам придется снова спрашивать пользователя.источник
Асинхронный TryGet шаблон:
Я полагаю, что для синхронных методов ответ @Johann Gerell - это шаблон, который следует использовать во всех случаях.
Однако шаблон TryGet с
out
параметром не работает с асинхронными методами.С C # 7's Tuple Literals теперь вы можете сделать это:
источник