Страницы

Поиск по вопросам

вторник, 2 октября 2018 г.

Что необходимо возвращать из функции: null или пустой объект?

Что является лучшей практикой при возвращении данных из функции. Лучше возвратить null или пустой объект? И почему необходимо использовать один вариант по сравнению с другим.
Рассмотрим следующий вариант:
public UserEntity GetUserById(Guid userId) { //Здесь код доступа к базе данных...
//Проверяем вернувшиеся данные и возвращаем null если ничего не найдено if (!DataExists) return null; //Или же я должен вернуть пустой объект? //return new UserEntity(); else return existingUserEntity; }
Перевод


Ответ

Выбор между null и null-объектом зависит от того, как метод будет использоваться.
Примеры
Получение коллекции пользователей
ICollection GetAllUsers();
Как этот метод будет использоваться? Скорее всего, примерно так:
foreach (User user in userService.GetAllUsers()) { // Обработка или отображение конкретного юзера }
Возвращение пустой коллекции в этом случае нам позволит сэкономить на одном if (foreach не умеет игнорировать null). Нам неважно, были какие-то элементы в коллекции или нет, логика вызывающего метода от этого обычно не зависит. Если же зависит, то мы всегда можем воспользоваться свойством Count
Получение одного пользователя
User GetUser(int id);
Как этот метод будет использоваться? Скорее всего, примерно так:
User user = userService.GetUser(id); if (user == null) { // Всё плохо, обработать ошибку } else { // Обработать или отобразить пользователя }
В этом случае разумно вернуть null, потому что наличие и отсутствие результата предполагает различное поведение вызывающего кода.
Чего точно не следует делать — это возвращать new User(): это не позволит нормально проверить, существует запрошенный пользователь или нет. Если уж делать null-объекты, то они по возможности должны быть в единственном экземпляре и неизменяемы.
Получение текущего пользователя
User GetCurrentUser();
Как этот метод будет использоваться? Скорее всего, примерно так:
User currentUser = userService.GetCurrentUser(); Console.WriteLine("{0} ({1})", currentUser.Name, currentUser.Level);
В этом случае обработка пользователя обычно не зависит от наличия или отсутствия реального пользователя, поэтому можно вернуть объект "пользователь-гость". В случае, если проверка требуется, то можно реализовать свойство вроде User.IsRegistered или User.AccessLevel
Планы на будущее
В C# 7 планируется добавить разделение между nullable и non-nullable для reference-типов (в дополнение к value-типам). Тогда методы выше будут выглядеть так:
ICollection GetAllUsers(); User? GetUser(int id); // Обратите внимание на знак вопроса User GetCurrentUser();
Если вызвать метод GetUser и не проверить на значение null, то компилятор выдаст предупреждение.
ReSharper
Если у вас есть R#, то вы можете добавить аннотации уже сейчас:
[NotNull, ItemNotNull] ICollection GetAllUsers(); [CanBeNull] User GetUser(int id); [NotNull] User GetCurrentUser();
Эти аннотации позволят анализатору R# явно разделять, что может быть null, а что не может, и за счёт этого предупреждать пользователя о потенциальных ошибках.

Комментариев нет:

Отправить комментарий