Почему люди используют магические значения вместо null в своем коде?

Я видел это в устаревшем коде и в некоторых проектах с открытым исходным кодом .NET. Я не могу представить себе причины для этого. Мне кажется намного проще использовать "null".

Пример:

public class Category
{
   int parentID;

   bool HasParent
   {
      get
      {
          return parentID != -1;
      }
   }
}

против

public class Category
{
   int parentID;

   bool HasParent
   {
      get
      {
          return parentID != null;
      }
   }
}

person Community    schedule 26.02.2009    source источник
comment
Было бы намного лучше, если бы вы показали хотя бы пример.   -  person GEOCHET    schedule 26.02.2009
comment
-1: без кода. -1: нет проблем. В чем проблема? Что не работает?   -  person S.Lott    schedule 26.02.2009
comment
Вы отменили проверку (должно быть parentID! = Null), но я думаю, что вопрос верный. +1   -  person erikkallen    schedule 26.02.2009
comment
Изменения сделали этот вопрос намного лучше, я не голосую против.   -  person DevinB    schedule 26.02.2009


Ответы (6)


Поскольку для получения значения NULL тип должен допускать значение NULL. Это отлично работает для ссылочных типов (любого определяемого вами класса и стандартной библиотеки), и если вы посмотрите, вы увидите, что люди действительно используют null всякий раз, когда у них есть ссылочный объект без значения.

Employee employee = Employees.Find("John Smith");
if(employee == null) throw new Exception("Employee not found");

Проблема возникает, когда вы используете такие типы value, как int, char или float. В отличие от ссылочных типов, которые указывают на блок данных где-то еще в памяти, эти значения хранятся и обрабатываются встроенно (нет указателя / ссылки).

Из-за этого по умолчанию типы значений не имеют нулевого значения. В предоставленном вами коде невозможно для parentID иметь значение null (я действительно удивлен, что это даже получено вашим компилятором - Visual Studio 2008 и, вероятно, 2005 г. нарисуют зеленую линию подчеркивания и сообщат вам, что утверждение всегда ложно).

Чтобы int имел нулевое значение, вам необходимо объявить его как допускающее значение NULL

int? parentID;

Теперь parentID может содержать нулевое значение, потому что теперь это указатель (ссылка на скважину) на 32-битное целое число, а не только на 32-битное целое число.

Надеюсь, вы понимаете, почему магические значения часто используются для представления null с базовыми типами (типами значений). Сохранение этих типов значений в качестве ссылки на значение, чтобы позволить им быть нулевыми, просто вызывает много проблем и часто приводит к значительному снижению производительности (узнайте, что такое упаковка / распаковка).

Изменить: для получения дополнительной информации о боксе / распаковке (что вам нужно, чтобы иметь int == null), см. Статью по адресу MSDN:

Упаковка и распаковка (Руководство по программированию на C #)

Представление

Что касается простых назначений, упаковка и распаковка - это дорогостоящие вычислительные процессы. Когда тип значения помещен в рамку, новый объект должен быть выделен и построен. В меньшей степени приведение, необходимое для распаковки, также требует больших вычислительных затрат. Для получения дополнительной информации см. «Производительность».

person David    schedule 26.02.2009
comment
Я не могу представить себе случай, когда этот очень небольшой прирост производительности будет критичным. - person Paco; 26.02.2009
comment
Накладные расходы немалые - вы говорите о добавлении операций упаковки / распаковки к основным типам данных и операциям. Тест, выполняющий операцию условного сложения целых чисел, выполняется в 9 раз медленнее, когда значения допускают значение NULL, из-за накладных расходов на упаковку. Попробуйте сами. - person David; 26.02.2009
comment
Я знаю, сколько времени это займет, но я не верю в оптимизацию алгоритмов O (N). - person Paco; 27.02.2009

Эти привычки часто развиваются при программировании на C. Многие программисты на C будут ужасно огорчены тратой обнуляемых целых чисел, которые, по крайней мере, требуют целого дополнительного бита и, возможно, даже указателя. Это особенно плохо, когда вы знаете, что значение будет положительным, и у вас есть все пространство отрицательных значений для использования в качестве флагов. -1 может означать, что не установлен, -2 может означать, что parentId даже не имеет смысла в контексте этого узла, -3 может означать, что у узла был родитель, который не мог справиться с работой и оставил запой и больше никогда не видел и т. д.

В мире C # чистота целых чисел, допускающих значение NULL (и их простая интеграция с RDMS), а также компьютеры с 2 ГБ + ОЗУ означают, что старые привычки C медленно умирают, но старые привычки умирают тяжело.

person Eclipse    schedule 26.02.2009

Наличие нулевого объекта может привести к возникновению исключений, тогда как коды ошибок (например, в случае сбоя «-1») не вызовут исключения.

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

person DevinB    schedule 26.02.2009

Работая с большими хранилищами данных, я иногда использую значение 0 вместо null, чтобы вставить ненулевое значение в свой куб. У меня всегда есть идентификатор 0, соответствующий значению «Неизвестно» во всех измерениях, поэтому у меня есть куб без нулевых значений.

основной интерес в нем - иметь более простые и эффективные запросы

person chburd    schedule 26.02.2009

Типы значений, допускающие значение NULL, доступны не для всех языков / сред; Они были добавлены в .Net 2.0. Если ваш унаследованный код основан на .Net и был запущен с использованием фреймворка 1.1, то ваш ответ заключается в том, что null в то время не подходил.

person Chris Shaffer    schedule 26.02.2009

Вы действительно заглянули в приложение, чтобы посмотреть? Пока магическое число хранится в одном месте с описательным именем, оно может быть весьма полезным.

person Andrew Hare    schedule 26.02.2009
comment
Да, они хранятся в одном месте с описательным значением, например Null.NullInteger и Null.NullDate, но я не мог понять, почему это полезно - person Paco; 26.02.2009
comment
Тогда это вообще не магическое значение - магические значения - это литералы в исходном коде, которые не имеют контекста и поэтому имеют неоднозначное значение. - person Andrew Hare; 26.02.2009
comment
Я считаю это магическим значением, потому что вы должны знать, что это не просто целое число или дата, но вы должны сравнить его с Null.NullObject, чтобы понять, что это означает. Вы не можете знать, что вам нужно сравнить его с nullobject, прежде чем использовать значение. - person Paco; 26.02.2009