Чи є нульові типи кращими до магічних чисел?


22

Останнім часом у мене були дебати з колегою. Ми спеціально використовуємо C #, але це може стосуватися будь-якої мови з нульовими типами. Скажімо, наприклад, у вас є значення, яке представляє максимум. Однак це максимальне значення необов’язкове. Я стверджую, що кращим буде нульове число. Мій колега виступає за використання нуля, посилаючись на прецедент. Зрозуміло, такі речі, як мережеві розетки, часто використовують нуль, щоб представити необмежений час очікування. Якби я сьогодні писав код, що стосується сокетів, я особисто використовував би нульове значення, оскільки вважаю, що краще би представляв факт відсутності тайм-ауту.

Яке представництво краще? Обидва вимагають перевірки умови на значення, що означає "none", але я вважаю, що тип, який міняється, передає наміри трохи краще.


6
Якщо використовується число, введіть його в константу, а не безпосередньо в код.
Ренато Діньяні

@ RenatoDinhaniConceição, що не може бути загальним правилом. В іншому випадку ви все закінчите програмним кодуванням .
Саймон Берго

Відповіді:


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це правда?
  • Якщо IsTimeoutEnabledпомилково, що станеться, якщо я встановив Duration100?

Це може призвести до декількох помилок. Уявіть наступний фрагмент коду:

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 - Danny Pflughoeft

+1 спеціально для "Створіть свій власний клас". Це те, що я б запропонував. Кожен раз, коли голий intнедостатньо виражає тип, щоб стримувати проблему, розгляньте нову структуру з додатковою інформацією (наприклад, const екземпляри структури, які представляють магічні значення, наприклад, або перерахунок на неї). Або розглянути контрактне програмування чи якісь інші рішення, але я думаю, що власна структура є найбільш простою.
CodexArcanum

12

Null не кращий за магічне число.

Важливо - НАЗВАЧАТИ значення, які мають магічні ефекти, якщо у вас є такі значення, і переконатися, що визначення цих імен є десь, що побачить кожен, хто наткнеться на магічне значення та wtf's.

if (timeout == 4298435) ... // bad.
if (timeout == null) ... // bad.
if (timeout == NEVER_TIME_OUT) ... // yay! puppies and unicorns!

2
Гаразд, можливо, це більше залежить від мови, але в C # ви, ймовірно, зробите: якщо (timeout.HasValue) замість прямого порівняння з null.
Метт H

2
Нуль не гірший за магічне число. З магічними числами ви ніколи не знаєте, що таке магічне число ... це може бути 0, -1 або щось інше. null просто null.
marco-fiset

9
Нульове значення означає відсутність значення. Це концепція, яку має намір висловити багато магічних чисел. Можливість використовувати null з нульовим типом - НАЙКРАЩЕ рішення, ніж вибір одного довільного значення з діапазону можливих значень для типу даних.
17 з 26

2
Використання "null" в якості свого магічного значення, якщо ваш тип має "null", це добре. Важливо - НАЗВАЧИТИ це, адже впевнений, що під час стрілянини наступний хлопець, який прийде разом, не знатиме, що ви мали на увазі. Null може означати "нескінченність", "ще не вказано", "помилка в коді, який створив структуру даних", або будь-яку кількість інших речей. Лише ім’я дає змогу наступному кодеру знати, що ви мали на увазі те значення, яке ви знаходитесь там, і яку поведінку ви мали на увазі.
mjfgates

1
@CodeInChaos: Я знаю, що ви можете зробити і те, і інше, але я віддаю перевагу HasValue. Я взагалі не є великим шанувальником null, але мінливі типи, що використовують HasValue, відчувають себе трохи ближче до Варіанту / Можливо, введіть мені, який я фанат.
Метт H

10

MAGIC_NUMBERКод слід абсолютно уникати, коли це можливо. nullє набагато чіткішим вираженням наміру.


6

У C # багато класів CLR мають статичний Emptyчлен:

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

Це не дає вам пам’ятати, чи потрібно використовувати магічне значення чи використовувати null для побудови порожнього об’єкта.

Але що робити, якщо ви маєте справу з простим типом значення, як-от int? У такому випадку подумайте, чи стаєте ви жертвою примітивного одержимості . Цілком можливо, що ваше, очевидно, просте числове властивість отримало б користь від власного класу чи структури, що дозволить вам вказати Emptyчлена, а також додати іншу поведінку, специфічну для такого виду значення.


3

У цьому випадку нульове значення - це чудовий спосіб вказати, що немає максимуму. Як правило, коли спеціальний випадок означає, що відповідне значення не застосовується, що ви просто не хочете, щоб функція, яку він налаштовує, null - це хороший показник цього.

Проблема з використанням null для представлення спеціальних випадків полягає в тому, що існує лише одне нульове значення, і може бути кілька спеціальних випадків. У цьому випадку я передавав би перерахунок як додатковий параметр, який може вказувати на окремий випадок або використовувати значення int нормально. (Це по суті те, що Nullable <> робить для вас, хоча він використовує булевий замість enum і об'єднує параметри в єдину структуру.)


3

У цьому випадку я думаю, що нульовий тип має ідеальний сенс.

Нульове значення означає відсутність значення. Це чітко інше поняття, ніж число, яке має значення 0.

Якщо ви хочете сказати "Якщо я не даю вам значення, використовуйте максимальне", тоді передача нуля - це правильний спосіб висловити це.


1

Null: загальне значення помилки, не вказане, недійсне або відсутність значення.

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

У контексті вашої проблеми timeoutInMillisecondsвластивість не є обов'язковою, і не згадується, що накладні витрати цього підходу дискваліфікують її як опцію.

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


0

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

залежно від середовища / мови, що використовується, нульова могла

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

MagicNumber завжди поводиться однаково.


0

Якщо ви забудете перевірити магічне число (це трапиться правильно), магічне число ще деякий час триватиме безглузді дані. Набагато краще мати нуль, який спричинить виняток якомога швидше.


-1

Null - не єдина альтернатива магічному числу.

public static int NO_TIMEOUT = 0;  // javaish

Нуль - це зло. У наведеному вище прикладі ви могли б уникнути цього, оскільки код, очевидно, міг би обробити нуль. Але загалом те, що відбувається, коли ви починаєте передавати нулі навколо, це те, що рано чи пізно ви отримуєте нульовий виняток вказівника. Це може статися, коли ви вперше пишете код, але код зберігається набагато довше, ніж лише перший випуск. Його часто підтримують люди, які не знають стільки про систему, як оригінальні розробники.

Scala (наприклад) має хорошу альтернативу в класі Option. Клас Опції має одне з двох значень: Деякі - яке обгортає значення, яке ви дійсно хочете, і Ніщо - яке не має значення.

Це робить очевидним для будь-якого розробника, що значення може не бути, і ви мали для цього кращий код. Ну, це все одно повинно зробити це очевидним.

І не всі магічні числа - це проблема. Залежно від контексту 0, 1, 1024 тощо може бути очевидним. 347? Так, цього вам слід уникати. :-)


4
-1: Обґрунтуйте "нуль - це зло".
deworde

4
Визначення псевдоніма для числа не змінює факту, що це все ще магічне число.
17 з 26

Ну, можливо, у вас є інше визначення магічного числа, ніж я. Будь ласка, дивіться en.wikipedia.org/wiki/…
Джон Страйер

1
Я згоден з Джоном Страйєром тут. Null - приклад ADT мовою, яка насправді не підтримує ADT. ОП може, ймовірно, піти звідси тут, але в цілому я вважаю, що будь-яка мова, яка не має сили, це програмісти трохи.
Джеремі Уолл
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.