Никогда не используйте Nulls?
В настоящее время мы продолжаем длительный процесс написания некоторых стандартов кодирования для С#.
Недавно я написал метод с сигнатурой
string GetUserSessionID(int UserID)
GetUserSession() возвращает null в случае, если сеанс не найден для пользователя.
в моем кодовом коде... Я говорю...
string sessionID = GetUserSessionID(1)
if (null == sessionID && userIsAllowedToGetSession)
{
session = GetNewUserSession(1);
}
В недавнем обзоре кода рецензент сказал: "Вы никогда не должны возвращать null из метода, поскольку он ставит больше работы над вызывающим методом для проверки нулей".
Сразу же я закричал, как будто вы вернули строку. Возможно, вам еще нужно выполнить какую-то проверку на возвращаемое значение.
if (string.Empty == sessionID)
Однако, подумав об этом, я никогда не верну null в случае коллекции/массива/списка. Я бы вернул пустой список.
Решение этой проблемы (я думаю) должно было бы реорганизовать это на 2 метода.
bool SessionExists(int userID);
и
string GetUserSessionID(int UserID);
На этот раз GetUserSessionID будет генерировать исключение SessionNotFound (так как он не должен возвращать null)
теперь код будет выглядеть как...
if(!SessionExists(1) && userIsAllowedToGetSession))
{
session = GetNewUserSession(1);
}
else
{
session = GetUserSessionID(1);
}
Теперь это означает, что нет нулей, но для меня это кажется немного более сложным. Это также очень простой пример, и мне было интересно, как это повлияет на более сложные методы.
Существует много рекомендаций по практической рекомендации о том, когда бросать исключения и как их обрабатывать, но, похоже, меньше информации об использовании null.
Есть ли у кого-нибудь еще какие-либо твердые рекомендации (или даже более высокие стандарты) относительно использования нулей и что это означает для типов с нулевым значением (мы вообще должны их использовать?)
Спасибо заранее,
Крис.
=====
Спасибо всем! Здесь много интересного.
Я дал ответ эгаге, поскольку мне нравится предложение Get vs Find в качестве руководства по кодированию, но все были интересными ответами.
Ответы
Ответ 1
Возможной практикой является использование префикса get для методов, которые генерируют исключение, если результат не найден, и найти префикс, если значение null возможно. Таким образом, легко видеть на стороне клиента, может ли код иметь проблему с нулевым значением.
Конечно, следует избегать нулей, и Андрей Хельсберг сказал в интервью, что если бы С# был создан сейчас, у него были бы более эффективные способы борьбы с недействительностью. http://www.computerworld.com.au/article/261958/-z_programming_languages_c?pp=3&fp=&fpid=
Ответ 2
нули определенно лучше, то есть более честны, чем "магические значения". Но они не должны возвращаться, когда произошла ошибка - что за исключения сделаны. Когда дело доходит до возвращения коллекций... лучше пустую коллекцию, чем null, я согласен.
Ответ 3
возвращает значение null в порядке, и следующее краткое и понятное:
var session = GetUserSessionID(1) ?? GetNewUserSession(1);
Ответ 4
По-моему, вы не должны исключать использование null в качестве возвращаемого значения. Я думаю, что это действительно во многих случаях. Но вы должны тщательно рассмотреть pro и con для каждого метода. Это полностью зависит от цели метода и ожиданий, которые могут возникнуть у вызывающих.
Я лично часто использую значение возвращаемого значения null в тех случаях, когда вы можете ожидать, что метод не возвращает abject (т.е. поиск базы данных с помощью первичного ключа, может возвращать ровно один экземпляр или none/null). Когда вы можете правильно ожидать, что метод вернет значение, вы должны использовать исключение.
В вашем конкретном примере я думаю, что это зависит от контекста системы. Если вызов, например, выполняется только из кода, в котором вы можете ожидать зарегистрированного пользователя, yo должен выдать исключение. Если, однако, вполне вероятно, что ни один пользователь не вошел в систему, и поэтому у вас нет идентификатора сеанса, вы должны выбрать null.
Ответ 5
Почему бы вам не использовать Null design pattern?
Ответ 6
Держите вещи Простыми. Сделайте его настолько безболезненным, насколько это возможно для потребителя вашего типа. Это компромисс: время для реализации и выгоды.
- Когда вы занимаетесь такими вещами, как выборка списков/коллекций, верните пустой список, как вы правильно указали.
- Аналогично для случаев, когда вам нужно вернуть значение "null", чтобы обозначить "не найденный" - попробуйте использовать шаблон Null Object для сильно используемого типа. Для редко используемых типов, я думаю, вы могли бы жить с несколькими проверками на null или -1 (String.IndexOf).
Если у вас есть больше категорий, отправьте в виде комментариев и я попытаюсь решить проблемы.
Ответ 7
Нули намного лучше, чем магические значения, и имеют гораздо больше смысла.
Вы также можете попробовать написать метод TryGetUserSession.
bool TryGetUserSession(int sessionId, out session)
Также попробуйте не писать nulls ==???, поскольку некоторым разработчикам сложнее читать.
С уважением,
Ответ 8
Я опасаюсь возвращать нули сам, хотя в вашем примере это, безусловно, кажется правильным.
Важно то, что clear имеет значение nullability как для аргументов, так и для возвращаемых значений. Это должно быть указано как часть вашей документации API, если ваш язык не может выразить эту концепцию напрямую. (Java не может, я читал, что С# может.) Если входной параметр или возвращаемое значение может быть нулевым, обязательно сообщите пользователям, что это означает в контексте вашего API.
Наша самая большая база кода - это Java-приложение, которое неуклонно растет за последнее десятилетие. Многие из внутренних API очень неясны в отношении их поведения WRT null. Это приводит к злокачественному росту нулевых проверок повсюду, потому что все программируют в обороне.
Особенно уродливые: функции, возвращающие коллекции, возвращающие null вместо пустой коллекции. WTF!? Не делай этого!
В случае строк обязательно различать строки, которые должны быть там, но могут быть пустыми и строками, которые действительно являются необязательными (могут быть пустыми). При работе с XML мы часто сталкиваемся с этим различием: элементы, которые являются обязательными, но могут быть пустыми, а не элементами, которые являются необязательными.
Одна часть системы, которая предоставляет запросы к структурам, подобным XML, для реализации бизнес-правил, очень радикальна в отношении того, что она является нулевой. Он использует шаблон Null Object повсеместно. В этом контексте это оказывается полезным, потому что оно позволяет вам делать такие вещи, как:
A.children().first().matches("Blah")
Идея состоит в том, что это выражение должно возвращать true, если первый дочерний элемент A называется "Blah". Он не будет разбиваться, если у A нет дочерних элементов, потому что в этом случае first() возвращает NullNode, который реализует интерфейс Node, но никогда не "соответствует" чему-либо.
Вкратце:
- Проясните значение и допустимость null в ваших API.
- Используйте свой мозг.
- Не будьте догматичны.
Ответ 9
Одним из решений может быть объявление простого типа возврата для таких вещей:
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 ==...)" стилем? Это совершенно не важно для С# - похоже, что кто-то привел в С# -кодирование С++-код!
Ответ 10
Я предпочитаю возвращать пустые коллекции вместо нулей, потому что это помогает избежать загромождения кода вызывающего абонента следующим образом:
if( list != null) {
foreach( Item item in list ) {
...
}
}
Ответ 11
Контрактный ответ по вашему запросу также будет моим решением:
if (myClass.SessionExists)
{
// Do something with myClass.Session
}
Ответ 12
Мы всегда возвращаем пустые списки/коллекции и не возвращаем NULL-списки/коллекции, потому что обработка пустых коллекций/списков снижает нагрузку на вызывающие функции.
Nullable - nullables особенно полезны, когда вы хотите избежать обработки "нулевых" значений из баз данных. Вы можете просто использовать GetDefaultOrValue для любого типа Nullable, не беспокоясь о том, что значение базы данных равно null в ваших бизнес-объектах. Мы определяем Nullables обычно ТОЛЬКО в наших бизнес-объектах, и это тоже, где мы действительно хотим избежать проверки нулевых значений db и т.д.
Ответ 13
Изобретатель нулей считает, что это плохая идея.
см.: http://lambda-the-ultimate.org/node/3186
Ответ 14
У меня была аналогичная проблема, хотя я через Exception и мне сказали, что я должен вернуть null,
Мы закончили читать
это сообщение в блоге о досадных исключениях
Я бы рекомендовал прочитать.
Ответ 15
NULL означает неизвестный/неопределенный.
Вам в основном нужна его при работе с базой данных, где данные просто "не определены (пока)". В вашем приложении NULL является хорошим типом возвращаемого значения для не исключительного, но нестандартного поведения.
Вы используете NULL, когда вывод не является ожидаемым, но недостаточно серьезным для исключения.