Что необходимо возвращать из функции: null или пустой объект?
1,00
р.
р.
Что является лучшей практикой при возвращении данных из функции. Лучше возвратить 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, а что не может, и за счёт этого предупреждать пользователя о потенциальных ошибках.