Никогда не используйте Nulls? - PullRequest
18 голосов
/ 14 мая 2009

В настоящее время мы проходим долгий процесс написания некоторых стандартов кодирования для C #.

Недавно я написал метод с подписью

string GetUserSessionID(int UserID)

GetUserSession () возвращает ноль в случае, если сеанс не найден для пользователя.

в моем коде вызова ... я говорю ...

string sessionID = GetUserSessionID(1)
if (null == sessionID && userIsAllowedToGetSession)
{
    session = GetNewUserSession(1);
}

В недавнем обзоре кода обозреватель сказал: «Вы никогда не должны возвращать нуль из метода, так как он добавляет больше работы к вызывающему методу для проверки на нули».

Сразу же я заплакал махинации, как будто вы возвращаете строку. Пусто, вам все равно нужно выполнить какую-то проверку возвращаемого значения.

if (string.Empty == sessionID)

Однако, подумав об этом, я никогда не верну ноль в случае Collection / Array / List. Я бы вернул пустой список.

Решением этого (я думаю) было бы рефакторинг этого в 2 метода.

bool SessionExists(int userID);

и

string GetUserSessionID(int UserID);

На этот раз GetUserSessionID вызовет исключение SessionNotFound (поскольку оно не должно возвращать ноль)

теперь код будет выглядеть так ...

if(!SessionExists(1) && userIsAllowedToGetSession))
{
   session = GetNewUserSession(1);
}
else
{
   session = GetUserSessionID(1);
}

Теперь это означает, что нет нулей, но мне это кажется немного сложнее. Это также очень простой пример, и мне было интересно, как это повлияет на более сложные методы.

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

Есть ли у кого-нибудь еще четкие рекомендации (или даже лучшие стандарты) в отношении использования нулей, и что это означает для типов, допускающих обнуляемость (мы должны их вообще использовать?)

Заранее спасибо,

Крис.

=====

Спасибо всем! Там очень много интересного обсуждения.

Я дал ответ egaga, так как мне нравится их предложение Get vs Find в качестве руководства по кодированию, но все они были интересными ответами.

Ответы [ 15 ]

29 голосов
/ 14 мая 2009

нули, безусловно, лучше, то есть более честны, чем "магические ценности". Но они не должны быть возвращены, когда произошла ошибка - для этого сделаны исключения. Когда дело доходит до возврата коллекций ... лучше пустая коллекция, чем ноль, я согласен.

11 голосов
/ 14 мая 2009

возврат нуля - это хорошо, а следующее краткое и простое для понимания:

var session = GetUserSessionID(1) ?? GetNewUserSession(1);
8 голосов
/ 14 мая 2009

Возможная практика - использовать префикс get для методов, которые выдают исключение, если результат не найден, и префикс find , если возможен ноль. Таким образом, на стороне клиента легко увидеть, может ли код иметь проблему с нулем.

Конечно, следует избегать нулей, и Андрей Хельсберг сказал в интервью, что если бы C # был создан сейчас, у него были бы лучшие способы борьбы с обнуляемостью. http://www.computerworld.com.au/article/261958/-z_programming_languages_c?pp=3&fp=&fpid=

5 голосов
/ 14 мая 2009

Почему бы вам не использовать Нулевой шаблон проектирования вместо этого?

5 голосов
/ 14 мая 2009

По моему мнению, вы не должны исключать использование нулевого значения в качестве возвращаемого значения. Я думаю, что это действительно во многих случаях. Но вы должны тщательно продумать за и против для каждого метода. Это полностью зависит от цели метода и ожиданий вызывающих абонентов.

Лично я часто использую нулевое возвращаемое значение в тех случаях, когда можно ожидать, что метод не возвращает объект (т. Е. Поиск в базе данных по первичному ключу может вернуть ровно один экземпляр или нет / ноль). Если вы можете ожидать, что метод вернет значение, вы должны использовать исключение.

В вашем конкретном примере я думаю, что это зависит от контекста системы. Если, например, вызов сделан только из кода, в котором вы можете ожидать входа в систему, вам следует сгенерировать исключение. Однако, если существует вероятность того, что ни один пользователь не вошел в систему и, следовательно, у вас нет идентификатора сеанса для возврата, вам следует выбрать возврат null.

4 голосов
/ 14 мая 2009

Сохраняйте вещи простыми. Сделайте это максимально безболезненно для потребителя вашего типа. Это компромисс: время для реализации и выгоды.

  • При работе с такими вещами, как выборка списков / коллекций, возвращайте пустой список, как вы правильно указали.
  • Аналогично для случаев, когда вам нужно вернуть значение «null» для обозначения «not-found» - попробуйте использовать шаблон Null Object для интенсивно используемого типа. Для редко используемых типов, я думаю, вы могли бы жить с несколькими проверками на ноль или -1 (String.IndexOf).

Если у вас есть больше категорий, пожалуйста, оставляйте комментарии, и я попытаюсь найти решение.

3 голосов
/ 14 мая 2009

Я сам опасаюсь возвращать нули, хотя в вашем примере это, безусловно, кажется правильным.

Что важно, так это ясно об обнуляемости как для аргументов, так и для возвращаемых значений. Это должно быть указано как часть документации по API, если ваш язык не может выразить эту концепцию напрямую. (Java не может, я читал, что C # может.) Если входной параметр или возвращаемое значение может быть нулевым, обязательно сообщите пользователям, что означает в контексте ваш API.

Нашей крупнейшей кодовой базой является Java-приложение, которое неуклонно растет в течение последнего десятилетия. Многие из внутренних API очень неясны по поводу их поведения WRT null. Это приводит к злокачественному росту нулевых проверок повсеместно, потому что все программируют в обороне.

Особенно уродливо: функции, которые возвращают коллекции, возвращающие ноль вместо пустой коллекции. WTF !? Не делай этого!

В случае строк обязательно различайте строки, которые должны быть там, но могут быть пустыми, и строки, которые действительно являются необязательными (могут быть нулевыми). При работе с XML мы часто сталкиваемся с этим различием: элементы, которые являются обязательными, но могут быть пустыми, по сравнению с элементами, которые являются необязательными.

Одна часть системы, которая обеспечивает запросы к XML-подобным структурам для реализации бизнес-правил, очень радикально относится к отсутствию нуля. Он использует Null Object Pattern везде. В этом контексте это оказывается полезным, потому что позволяет вам делать такие вещи, как:

A.children().first().matches("Blah")

Идея состоит в том, что это выражение должно возвращать true, если первый дочерний элемент A называется "Blah". Не произойдет сбой, если у A нет дочерних элементов, потому что в этом случае first () возвращает NullNode, который реализует интерфейс Node, но никогда ничего не «сопоставляет».

В итоге:

  • Проясните значение и допустимость значения null в своих API.
  • Используй свой мозг.
  • Не будь догматичным.
3 голосов
/ 14 мая 2009

Нули намного лучше, чем магические значения и имеют гораздо больше смысла. Вы также можете попробовать написать метод TryGetUserSession. </p> <p>bool TryGetUserSession(int sessionId, out session)

Также старайтесь не писать нули == ???, так как некоторым разработчикам труднее читать.

С уважением,

2 голосов
/ 14 мая 2009

Я предпочитаю возвращать пустые коллекции вместо нулей, потому что это помогает избежать загромождения кода вызывающей стороны, как показано ниже:

if( list != null) {
    foreach( Item item in list ) {
         ...
    }
}
2 голосов
/ 14 мая 2009

Одним из решений может быть объявление простого типа возврата для таких вещей:

class Session
{
    public bool   Exists;
    public string ID;
}

Session GetUserSession(int userID) {...}

...

Session session = GetUserSessionID(1);

if (session.Exists)
{
    ... use session.ID
}

Мне вообще нравится избегать нулей, но строки - это особый случай. Я часто заканчиваю тем, что вызываю string.IsNullOrEmpty (). Настолько, что мы используем метод расширения для строки:

static class StringExtensions
{
    public static bool IsNullOrEmpty(this string value)
    {
        return string.IsNullOrEmpty(value);
    }
}

Тогда вы можете сделать:

string s = ... something

if (s.IsNullOrEmpty())
{
 ...
}

Кроме того, поскольку мы говорим о стиле кодирования:

Что за неестественно выглядящий стиль if (null == ...)? Это совершенно не нужно для C # - похоже, что кто-то принес багаж C ++ в стили кодирования C #!

...