Обнуляемые типы предпочтительнее магических чисел?

22

В последнее время у меня была небольшая дискуссия с коллегой. Мы специально используем C #, но это может относиться к любому языку с обнуляемыми типами. Скажем, например, у вас есть значение, которое представляет максимум. Однако это максимальное значение не является обязательным. Я утверждаю, что обнуляемое число будет предпочтительнее. Мой коллега поддерживает использование нуля, ссылаясь на прецедент. Конечно, такие вещи, как сетевые сокеты, часто используют ноль для представления неограниченного времени ожидания. Если бы я писал сегодня код, работающий с сокетами, я бы лично использовал значение, которое можно обнулять, так как я чувствую, что это будет лучше отражать тот факт, что тайм-аут НЕТ.

Какое представление лучше? Оба требуют проверки условия для значения, означающего «нет», но я считаю, что обнуляемый тип передает намерение немного лучше.

Мэтт Н
источник
6
Если используется число, поместите его в константу, а не прямо в коде.
Ренато Динхани
@ RenatoDinhaniConceição, который не может быть общим правилом. В противном случае вы в конечном итоге softcoding все.
Саймон Бергот

Ответы:

24

Рассмотреть возможность:

  • Язык,

  • Фреймворк,

  • Контекст.

1. Язык

Использование ∞ может быть решением для максимума.

  • JavaScript, например, имеет бесконечность. C # не ¹

  • Ада, например, имеет диапазоны. C # нет.

В C # есть int.MaxValue, но вы не можете использовать его в вашем случае. int.MaxValueмаксимальное целое число, 2 147 483 647. Если в вашем коде у вас есть максимальное значение чего-либо, например максимально допустимое давление, прежде чем что-то взорвется, использование 2 147 483 647 не имеет смысла.

2. Рамки

.NET Framework довольно противоречив в этом вопросе, и его использование магических ценностей может быть подвергнуто критике.

Например, "Hello".IndexOf("Z")возвращает магическое значение -1. Это может облегчить (не так ли) манипулирование результатом:

int position = "Hello".IndexOf("Z");
if (position > 0)
{
    DoSomething(position);
}

вместо использования пользовательской структуры:

SearchOccurrence occurrence = "Hello".IndexOf("Z");
if (occurrence.IsFound)
{
    DoSomething(occurrence.StartOffset);
}

но совсем не интуитивно. Почему -1и нет -123? Начинающий может также ошибочно думать, что это 0означает «Не найден» или просто опечатка (position >= 0).

3. Контекст

Если ваш код связан с таймаутами в сетевых сокетах, использование чего-то, что использовалось всеми в течение десятилетий для обеспечения согласованности, не является плохой идеей . Тем более, 0что тайм-аут очень ясен: это значение, которое не может быть нулевым. Использование собственного класса в этом случае может усложнить понимание:

class Timeout
{
    // A value indicating whether there is a timeout.
    public bool IsTimeoutEnabled { get; set; }

    // The duration of the timeout, in milliseconds.
    public int Duration { get; set; }
}
  • Могу ли я установить Duration0, если IsTimeoutEnabledэто правда?
  • Если IsTimeoutEnabledfalse, что произойдет, если я установлю Durationна 100?

Это может привести к множественным ошибкам. Представьте себе следующий фрагмент кода:

this.currentOperation.Timeout = new Timeout
{
    // Set the timeout to 200 ms.; we don't want this operation to be longer than that.
    Duration = 200,
};

this.currentOperation.Run();

Операция длится десять секунд. Можете ли вы увидеть, что не так с этим кодом, не читая документацию Timeoutкласса?

Вывод

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

  • int.MaxValueтесно связан с самим языком. Не используйте int.MaxValueдля ограничения максимальной скорости Vehicleкласса или максимально приемлемой скорости для самолета и т. Д.

  • Избегайте магических значений, как -1в вашем коде. Они вводят в заблуждение и приводят к ошибкам в коде.

  • Создайте свой собственный класс, который будет более простым, с указанием минимальных / максимальных значений. Например VehicleSpeedможет иметь VehicleSpeed.MaxValue.

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

  • Не забудьте смешать подходы. Например:

    class DnsQuery
    {
        public const int NoTimeout = 0;
    
        public int Timeout { get; set; }
    }
    
    this.query.Timeout = 0; // For people who are familiar with timeouts set to zero.
    // or
    this.query.Timeout = DnsQuery.NoTimeout; // For other people.
    

¹ Вы можете создать свой собственный тип, который включает в себя бесконечность. Здесь я говорю только о нативном intтипе.

Арсений Мурзенко
источник
1
«использование чего-то, что использовалось всеми в течение десятилетий ради согласованности, - это не плохая идея» / «Не следуйте никаким предыдущим указаниям и не используйте магические ценности, если это десятилетие является общим соглашением в очень специфической области, используемой большинство людей пишут код в этой области. " - Где-то есть опечатка, я думаю?
deworde
1
@ Deworde Я думаю, что MainMa имеет в виду руководящие принципы, которые он сам дал выше этого.
Джошуа Дрейк
1
Я не согласен с примером indexOf, так как -1 находится за пределами строки, что Z, безусловно, является.
Джошуа Дрейк
5
«У JavaScript, например, бесконечность. C # - нет». - а?
BlueRaja - Дэнни Пфлугхофт
+1 специально для «Создай свой собственный класс», что я бы предложил. В любое время, когда голое intнедостаточно выражает тип, чтобы ограничить проблему, рассмотрите новую структуру с большим количеством информации (например, const экземпляры структуры, которые представляют магические значения, или перечисление на нем для указания). Или подумайте о программировании по контракту или о некоторых других решениях, но я думаю, что пользовательская структура является наиболее простой.
CodexArcanum
12

Нуль не лучше магического числа.

Важно НАИМЕНОВАТЬ значения, которые имеют магические эффекты, если вам нужно иметь такие значения, и убедиться, что определения этих имен находятся там, где их увидит любой, кто сталкивается с магическим значением и wtf.

if (timeout == 4298435) ... // bad.
if (timeout == null) ... // bad.
if (timeout == NEVER_TIME_OUT) ... // yay! puppies and unicorns!
mjfgates
источник
2
Хорошо, возможно, это зависит больше от языка, но в C # вы, скорее всего, сделаете: if (timeout.HasValue) вместо прямого сравнения с нулем.
Мэтт Х
2
Ноль не хуже магического числа. С магическими числами вы никогда не узнаете, что такое магическое число ... это может быть 0, -1 или что-то еще. ноль просто ноль.
marco-fiset
9
Нуль означает отсутствие значения. Это концепция, которую намеревается выразить множество магических чисел. Возможность использовать значение NULL с типом, допускающим значение NULL, является НАМНОГО лучшим решением, чем выбор одного произвольного значения из диапазона возможных значений для типа данных.
17 из 26
2
Использование «ноль» в качестве магического значения, если ваш тип имеет «ноль», хорошо. Важно, чтобы НАИМЕНОВАТЬ это, потому что, конечно, стреляя в следующего парня, который не придет, не поймет, что вы имели в виду. Нуль может означать «бесконечность», «еще не определено», «ошибка в коде, который создал структуру данных», или любое другое число. Только имя позволяет следующему кодировщику знать, что вы имели в виду это значение, и какое поведение вы хотели, чтобы оно вызывало.
mjfgates
1
@CodeInChaos: я знаю, что вы можете сделать и то и другое, но я предпочитаю HasValue. Я вообще-то не большой поклонник null в целом, но обнуляемые типы, использующие HasValue, кажутся мне немного ближе к типу Option / Maybe, фанатом которого я являюсь.
Мэтт Х
10

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

DeadMG
источник
6

В C # многие классы CLR имеют статический Emptyчлен:

  • System.String.Empty
  • System.EventArgs.Empty
  • System.Guid.Empty
  • System.Drawing.Rectangle.Empty
  • System.Windows.Size.Empty

Это избавляет вас от необходимости помнить, использовать ли магическое значение или использовать ноль для создания пустого объекта.

Но что, если вы имеете дело с простым типом значения, таким как int? В этом случае подумайте, не стали ли вы жертвой Primitive Obsession . Вполне возможно, что ваше внешне простое числовое свойство выиграет от своего собственного класса или структуры, что позволит вам указать Emptyчлен, а также добавить другое поведение, специфичное для этого типа значения.

Kyralessa
источник
3

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

Проблема использования нулевого значения для представления особых случаев заключается в том, что существует только одно нулевое значение и может быть несколько особых случаев. В этом случае я бы передал перечисление в качестве дополнительного параметра, который может указывать на особый случай или на обычное использование значения int. (По сути, это то, что Nullable <> делает для вас, хотя он использует логическое значение вместо перечисления и объединяет параметры в единую структуру.)

JGWeissman
источник
3

В этом случае я думаю, что обнуляемый тип имеет смысл.

Нуль означает отсутствие значения. Это отчетливо отличное понятие от числа со значением 0.

Если вы хотите сказать «Если я не даю вам значение, используйте максимум», тогда передача значения null - это правильный способ выразить это.

17 из 26
источник
1

Нуль: общее значение ошибки, неопределенное, недействительное или отсутствие значения.

Ноль: фактическое, но не обязательно логическое или интуитивное значение (в этом контексте). Также распространенное значение при инициализации.

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

Вывод: существуют исключения, и решения различаются в зависимости от языка и области; в этом случае я бы выбрал Null. Где (я полагаю) некоторые люди ошибаются, когда плохо разделяют данные от интерфейса. Они просто ожидают, что любой клиент прочитает документацию (или реализацию), чтобы определить, как эти специальные значения должны использоваться / обрабатываться - особые случаи просачиваются в программу клиента, и это может быть совершенно неясно. Добавив хороший уровень абстракции, использование может быть намного понятнее.

джастин
источник
0

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

в зависимости от используемой среды / языка null может

  • просто быть 0
  • не может быть юридической ценностью
  • может привести к неожиданным результатам из-за трехсторонней логики

MagicNumber всегда ведет себя одинаково.

Ryathal
источник
0

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

Том Хотин - Tackline
источник
-1

Нуль не единственная альтернатива магическому числу.

public static int NO_TIMEOUT = 0;  // javaish

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

У Scala (например) есть хорошая альтернатива в классе Option. Класс Option имеет одно из двух значений: Some - которое оборачивает значение, которое вы действительно хотите, и None - которое не имеет значения.

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

И не все магические числа являются проблемой. В зависимости от контекста 0, 1, 1024 и т. Д. Все может быть очевидным. 347? Да, этого вам следует избегать. :-)

Джон Стрейер
источник
4
-1: Обоснуйте "ноль есть зло".
deworde
4
Определение псевдонима для числа не меняет того факта, что это все еще магическое число.
17 из 26
Ну, возможно, у вас есть другое определение магического числа, чем у меня. Пожалуйста, смотрите en.wikipedia.org/wiki/…
Джон Страйер
1
Я согласен с Джоном Стрейером здесь. Null является примером ADT на языке, который фактически не поддерживает ADT. OP может, вероятно, сойти с рук здесь, но в целом я считаю, что любой язык, который имеет null, слегка провалился, это программисты.
Джереми Уолл