Це правильна ситуація для використання константи?


42

Тож мій професор давав відгуки про проект, над яким я працював. Він стикував кілька знаків для цього коду:

if (comboVendor.SelectedIndex == 0) {
  createVendor cv = new createVendor();
  cv.ShowDialog();
  loadVendors();
}

Це в оброблюваному списку "індекс змінено". Він використовується, коли користувач хоче створити нового постачальника, мій верхній варіант (індекс 0, який ніколи не змінюється) відкриє діалогове вікно "Створити нового постачальника". Тож вміст мого комбінованого вікна виглядає так:

Create New Vendor...
Existing Vendor
Existing Vendor 2
Existing Vendor 3

Його проблема полягає в першому коді рядка:

if (comboVendor.SelectedIndex == 0)

Він стверджує, що 0 повинен бути постійним, і насправді через це прикріпив мені оцінки. Він стверджує, що я взагалі не повинен використовувати літерали у своєму коді.

Справа в тому, що я не розумію, чому я хотів би зробити цей код у цій ситуації постійним. Цей індекс ніколи не зміниться, і це не те, що вам потрібно буде налаштувати. Здається марно пам'ять зберігати єдиний 0 в пам'яті, який використовується в дуже конкретній ситуації і ніколи не змінюється.


32
Він догматичний. Магічні числа взагалі добре уникати. Я думаю, що -1, 0 і 1 можна вважати винятками з цього правила. Крім того, така константа не займе більше місця, ніж буквальна 0.
Дейв Муні

23
@DaveMooney: Ви впевнені, що у вас тут не виникає реакції на коліна? Це правда , що речі , як -1в str.indexOf(substr) != -1протягом « strмістить substr» це втирають виправдано. Але тут значення 0 не є ні очевидним (яке відношення до створення нового постачальника?), Ні по-справжньому постійним (що, якщо зміниться спосіб створення нового постачальника?).

62
ви повинні вивчити правила, перш ніж дозволити їх порушувати
Рятхал

12
У мене цей метод несподівано провалився. Список був відсортований за алфавітом. Я використовував - - Створювати нове - - щоб тире сортував перший і жорстко закодований індекс 0 до методу CreateNew. Потім хтось додав предмет, починаючи з однієї цитати «Мій предмет», яка сортує перед тире. Моє жорстке кодування призвело до краху програми при наступному завантаженні списку. Мені довелося вручну змінити файл даних списку, щоб відновити дані клієнта.
Hand-E-Food

14
Ви можете скористатися для int.Zeroтого, щоб зробити його щасливим :)
Пол Стовелл

Відповіді:


90

Дійсний правильний спосіб зробити це в C # - це взагалі не покладатися на замовлення ComboItems .

public partial class MyForm : Form
{
    private readonly object VENDOR_NEW = new object();

    public MyForm()
    {
        InitializeComponents();
        comboVendor.Items.Insert(0, VENDOR_NEW);
    }

    private void comboVendor_Format(object sender, ListControlConvertEventArgs e)
    {
        e.Value = (e.ListItem == VENDOR_NEW ? "Create New Vendor" : e.ListItem);
    }

    private void comboVendor_SelectedIndexChanged(object sender, EventArgs e)
    {
        if(comboVendor.SelectedItem == VENDOR_NEW)
        {
            //Special logic for selecting "create new vendor"
        }
        else
        {
            //Usual logic
        }
    }
}

22
Ну, якщо це C #, вам не слід використовувати ALL_CAPS для констант. Константи повинні бути PascalCased - stackoverflow.com/questions/242534 / ...
Groky

4
@Groky: Чому це має значення? Кому байдуже, як він називає своїх констант? Це на 100% правильно, якщо він використовує ALL_CAPS послідовно для констант.
marco-fiset

4
@marcof: Ну якщо константи є частиною загальнодоступного інтерфейсу, то він повинен дотримуватися вказівок щодо іменування MS. Якщо ні, то він, принаймні, повинен навчитися кращій практиці рано.
Groky

2
Це все ще неправильно. Він зміщує проблему з наявності цілого літералу (нуля) до рядкового літералу (Створити нового постачальника). У кращому випадку проблема полягає в тому, що "що, якщо мітка зміниться", а не "що, якщо зміниться індекс".
Фрейхейт

7
@Freiheit: Неправильно. Постійна константа тут лише для відображення; це ніяк не впливає на логіку програми. На відміну від використання магічних значень / рядків для зберігання стану програми, зміна цього рядка (або додавання / видалення речей зі списку) ніколи нічого не може порушити.
BlueRaja - Danny Pflughoeft

83

Порядок у комбінованому полі може змінитися. Що робити, якщо ви додасте інший варіант, наприклад "Створити спеціального постачальника ..." перед вашим "Створити нового вендера ..."

Перевага використання константи полягає в тому, що існує багато методів, які залежать від порядку комбінованого поля, вам потрібно змінити лише константу, а не всі методи, якщо це все зміниться.

Використання константи також легше читати, ніж буквальне.

if (comboVendor.SelectedIndex == NewVendorIndex)

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


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

1
@Chad Я погоджуюся, що визначення контролю за наказом у gui дуже крихке. Якщо мова підтримує додавання значення елементу gui, який можна переглянути, я би скористався цим.
Майкл Круссел

3
@ рішення, тому що це конвенція.
Іке

3
@Ikke Це, безумовно, не конвенція. stackoverflow.com/questions/242534/…
SolutionYogi

1
Якщо ми говоримо про C #, то NewVendorIndex є умовою. Це відповідає решті стилю .NET.
MaR

36

Ця ситуація, яку ви описуєте, - це виклик судження, я особисто не використовував би її, якщо вона використовується лише один раз і вже читається.

Справжньою відповіддю є те, що він вибрав це, щоб навчити вас урок.

Не забувайте, що він є професором, його робота полягає в тому, щоб навчити вас кодуванню та передовим практикам.

Я б сказав, що він насправді робить досить гарну роботу.

Впевнений, що він може вийти трохи абсолютним, але я впевнений, що ви подумаєте ще раз, перш ніж використовувати магічні числа.

Крім того, він потрапив вам під шкіру достатньо, щоб ви могли приєднатися до інтернет-спільноти про програмістів, аби лише з'ясувати, що вважається найкращою практикою в цій ситуації.

Капелюхи до вашого професора.


+1 за вказівку, що в цьому випадку засоби виправдовують кінцевий результат
oliver-clare

@ Thanos- " Не забувайте, що він є професором, його робота полягає в тому, щоб навчити вас кодуванню та передовим практикам". У мого професора цього ніколи не було. Я б сказав, що його завдання полягає в тому, щоб навчити вас те, що кафедра вважає важливим після створення курсу.
Рамхаунд

13

[...] мій головний варіант (індекс 0, який ніколи не змінюється) відкриває діалогове вікно "Створити нового постачальника".

Той факт, що вам довелося пояснювати, підтверджує, чому слід використовувати константу. Якщо ви ввели константи, як-от NEW_VENDOR_DIALOG, ваш код буде більш зрозумілим. Крім того, компілятори оптимізують константи, так що не буде змін у продуктивності.

Пишіть програми для програмістів, а не компіляторів. Якщо ви спеціально не намагаєтеся мікро-оптимізувати, що, здається, не є.


2
-1 для навіть згадування продуктивності. Навіть якщо комп'ютер не оптимізований, він може робити такі мільярди операцій, перш ніж користувач помітить.
Борис Янков

3
@Boris OP здавалося стурбованим витрачанням ресурсів, саме тому я згадував про це. Я не бачу, наскільки моя відповідь була б менш правильною через це.
kba

12

Він стверджує, що 0 повинен бути постійним, і насправді через це прикріпив мені оцінки.

Я погодився б. Використання нуля тут - "магія". Уявіть, що ви читаєте цей код вперше. Ви не знаєте, чому нуль особливий, і буквальний нічого не говорить вам про те, чому нуль особливий. Якщо замість цього ви сказали, if(comboVendor.SelectedIndex == CreateNewVendorIndex)то першому читачеві стає надзвичайно зрозуміло, що означає код.

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

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

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

Те, що він ніколи не зміниться, - чудова причина зробити його постійним . Ось чому постійні називають константами; тому що вони ніколи не змінюються.

і це не те, що вам потрібно було б підправити.

Дійсно? Ви не бачите жодної ситуації, в якій хтось може захотіти змінити порядок речей у комбінованому полі?

Те, що ви бачите причину, чому це може змінитися в майбутньому, є вагомою причиною не робити це постійним. Швидше, це повинно бути непостійним читанням статично ціле поле. Постійною має бути кількість, яка гарантовано залишатиметься однаковою протягом усього часу . Пі і атомне число золота - хороші константи. Номери версій не є; вони змінюють кожну версію. Ціна золота, очевидно, жахлива константа; вона змінюється щосекунди. Робіть лише постійні речі, які ніколи, ніколи не змінюються .

Здається марно пам'ять зберігати єдиний 0 в пам'яті, який використовується в дуже конкретній ситуації і ніколи не змінюється.

Тепер ми підходимо до суті справи.

Це, мабуть, найважливіший рядок у вашому запитанні, оскільки це вказує на те, що ви маєте глибоке хибне розуміння (1) пам'яті та (2) оптимізації. Ви в школі навчаєтесь, і зараз би чудовий час, щоб правильно зрозуміти основи. Чи можете ви детально пояснити, чому ви вважаєте, що "марно пам'ять зберігати єдиний нуль в пам'яті"? По-перше, чому ви вважаєте, що оптимізація використання чотирьох байтів пам’яті в процесі, що має щонайменше два мільярди байт зберігання, адресованого користувачеві, є актуальним? По-друге, саме який ресурс, на вашу думку, тут споживається ? Що ви маєте на увазі під споживанням "пам'яті"?

Мене цікавлять відповіді на ці питання, по-перше, тому що вони дають вам змогу дізнатися, як ваше розуміння оптимізації та управління пам’яттю є неправильним, а по-друге, тому що я завжди хочу знати, чому початківці вірять у химерні речі, щоб я міг спроектувати кращі інструменти, щоб навести їх на правильне переконання.


6

Він правий. Ти правий. Ви помиляєтеся.

Він концептуально має рацію, що слід уникати магічних чисел. Константи роблять код більш читабельним, додаючи контекст до того, що означає число. Надалі, коли хтось читає ваш код, він знає, чому саме використовується певна цифра. І якщо вам потрібно змінити значення десь внизу лінії, набагато краще змінити його в одному місці, а не намагатися вишукувати всюди, де використовується певне число.

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

Ти не помилився, вважаючи, що константа переноситься як змінна, "використовуючи пам'ять". Постійна є для людини і компілятора. Він повідомляє компілятору поставити це значення на це місце під час компіляції, де ви інакше поставили буквальне число. І навіть якщо це несе постійну пам'ять, для всіх, крім найвимогливіших програм, втрата ефективності навіть не може бути вимірюваною. Турбота про використання однієї цілої пам'яті в пам'яті, безумовно, потрапляє в "передчасну оптимізацію".


1
Я майже проголосував би за цю відповідь, якби не третій абзац, стверджуючи, що використання буквального 0 досить зрозуміле і жодна константа не гарантована. Набагато кращим рішенням було б взагалі не покладатися на індексацію елемента комбінованого вікна, а скоріше на значення вибраного елемента. Магічні константи - це магічні константи, навіть якщо вони дорівнюють 0 або 3,14 або нічого іншого - називайте їх відповідним чином, оскільки це робить код більш читабельним.
Roland Tepp

Можливо, краще використовувати значення в списку, але це було не в чому його питання. Його питання полягав у тому, чи це було правильним використанням для постійної. І якщо порівнювати з 0 в контексті взаємодії з графічним інтерфейсом (з будь-якої причини - можливо, хтось шукає кулаковий предмет незалежно від вартості), я думаю, було б зайвим використовувати константу в цьому місці.
GrandmasterB

Я б не погодився ... Якщо просто подивитись на код, ніколи не стає очевидно, яке значення 0 - це може бути насправді, що він завжди шукає перший пункт у списку, і це все, але потім це читав би набагато чистіше, якщо замість цього було б "comboVendor.SelectedIndex == FirstIndex"?
Роланд Тепп

2

Я б замінив 0з константою , щоб сенс ясно, наприклад NewVendorIndex. Ви ніколи не знаєте, чи зміниться ваше замовлення.


1

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

Гарне навчання для корпоративного світу? Цілком можливо.


2
Я не погоджуюся з частиною "використовувати константу лише в тому випадку, якщо літерал буде використовуватися кілька разів". З 0 (а може бути, 1, 1) це часто зрозуміло, в більшості випадків добре давати ім’я речі, яка набагато чіткіше хитро читає код.
johannes

1

Якщо чесно, то, хоча я не думаю, що ваш код є найкращою практикою, його пропозиція відверто трохи гротескна.

Більш поширеною практикою для комбінації .NET є надання елемента "Вибрати .." порожнім значенням, а фактичні елементи мають значущі значення, а потім виконайте:

if (string.IsNullOrEmpty(comboVendor.SelectedValue))

а не

if (comboVendor.SelectedIndex == 0)

3
У вашому прикладі null просто інший буквальний. Основою цього уроку є те, що слід уникати літералістів.
завищено

@overslacked - У моєму прикладі немає нульового літералу.
Carson63000

Навіть якщо був нульовий літерал, нуль є прийнятним для використання літералом, я навіть не вірю, що існує спосіб перевірити, чи є посилання на об’єкт нульовим, не використовуючи перевірку, чи дорівнює йому нуль.
Рамхаунд

Carson63000 - Я мав на увазі ваше використання IsNullOrEmpty. Ви поміняєте одну "магічну цінність" на іншу. @Ramhound - Null є прийнятним літералом у деяких випадках, жодних питань, але я не вважаю, що це хороший приклад (використовуючи null або blank як магічне значення).
завищено

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

1

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

Однак його точка хороша. Це може бути його спосіб зрозуміти переваги констант:

1-Вони захищають ваш код певною мірою від випадкових підробок

2-Як говорить у своїй відповіді @DeadMG, якщо одне буквальне значення використовується в багатьох місцях, воно може помилково з’являтися з іншим значенням - значить, константи зберігають послідовність.

3-константи зберігають тип, тому вам не доведеться використовувати щось на зразок 0F, щоб означати нуль.

4-Для зручності читання COBOL використовує ZERO в якості зарезервованого слова для значення нуля (але також дозволяє використовувати буквальний нуль). Отже, надання значення імені іноді корисно, наприклад: (Джерело: ms-Constants

class CalendarCalc
{
    const int months = 12;
    const int weeks = 52; //This is not the best way to initialize weeks see comment
    const int days = 365;

    const double daysPerWeek = (double) days / (double) weeks;
    const double daysPerMonth = (double) days / (double) months;
}

або як у вашому випадку (як показано у відповіді @Michael Krussel)


2
Хіба це не буде дивним визначенням днів на тиждень? Є 7 днів на тиждень не 7.4287143
Вінстон Еверт

@WinstonEwert, дякую за ваш коментар 365/52 = 7.01923076923077 = 7 + (1/52). Тепер, якщо ви вилучите дробову частину і обчислите 7 * 52, ви отримаєте 364 дні, що не є правильною кількістю днів у році. Мати дріб точніше, ніж втратити його (оскільки ви можете відформатувати результат, щоб він показав 7, якщо ви хочете відобразити лише число). У будь-якому випадку це був лише приклад з MS щодо констант, але ваш пункт цікавий.
NoChance

1
Звичайно, це лише приклад, але я заперечую проти вашого твердження, що його більш точно включати дріб. Тиждень визначається як 7 днів. З огляду на ваше визначення, 26 тижнів становить 182,5 днів, що просто не точно. Дійсно, проблема у вас int weeks = 52, 52 тижні на рік не існує. За рік припадає 52.142857142857146 тижнів, і це число, за яким слід тримати дроби. Звичайно, єдине, що насправді є постійним у цілому наборі констант кількість місяців.
Вінстон Еверт

0

Потрібно поставити його в константу лише в тому випадку, якщо він має складну деривацію або якщо це повторюється часто. Інше, буквальне - це добре. Поставити все в постійне - це тотальний перебір.


Тільки якщо ви розглядаєте двічі часто і вам ніколи не потрібно змінювати код. Створити помилки дуже просто, змінивши один з двох випадків.
BillThor

0

Насправді, як згадувалося, що робити, якщо зміниться позиція? Що ви могли / повинні зробити, це використовувати код, а не покладатися на індекс.

Отже, коли ви створюєте свій список вибору, він закінчується html на зразок

<select>
    <option value='CREATE'>Create New Vendor...</option>
    <option value='1'>Existing Vendor</option>
    <option value='2'>Existing Vendor 2</option>
    <option value='3'>Existing Vendor 3</option>
</select>

Потім замість того, щоб перевірити selectedIndex === 0, перевірте, чи є значення, CREATECODEяке було б у постійній формі, і було б використано як для цього тесту, так і при створенні списку вибору.


3
Можливо, вдалий підхід, але бачачи, що він використовує C #, html - це не самий надійний приклад коду.
Вінстон Еверт

0

Я б позбувся цього взагалі. Просто поставте нову кнопку біля списку списків. Двічі клацніть елемент у списку для редагування або натисніть кнопку. Не зберігайте нову функціональність у коробці. Потім магічне число видаляється взагалі.

Взагалі будь-яке буквальне число в коді повинно бути визначене як константа, щоб поставити контекст навколо числа. Що означає нуль? У цьому випадку 0 = NEW_VENDOR. В інших випадках це може означати щось інше, тому завжди зручно для читабельності та ремонтопридатності поставити навколо цього якийсь контекст.


0

Як уже говорили інші, вам слід скористатись іншим способом, крім номера індексу, для ідентифікації елемента комбінації, який відповідає даній дії; або, ви можете знайти індекс з деякою програмною логікою і зберегти його у змінній.

Причину, про яку я пишу, - звернутися до вашого коментаря щодо "використання пам'яті". У C #, як і в більшості мов, константи "складені" компілятором. Наприклад, складіть наступну програму та вивчіть ІЛ. Ви побачите, що всі ці числа навіть не входять в ІЛ, не кажучи вже про пам'ять комп'ютера:

public class Program
{
    public static int Main()
    {
        const int a = 1000;
        const int b = a + a;
        const int c = b + 42;
        const int d = 7928345;
        return (a + b + c + d) / (-a - b - c - d);
    }
}

отриманий ІЛ:

.method public hidebysig static 
    int32 Main () cil managed 
{
    .maxstack 1
    .locals init (
        [0] int32 CS$1$0000
    )

    IL_0000: nop
    IL_0001: ldc.i4.m1  // the constant value -1 to be returned.
    IL_0002: stloc.0
    IL_0003: br.s IL_0005

    IL_0005: ldloc.0
    IL_0006: ret
}

Отже, чи використовуєте ви константний, буквальний або кілобайт коду, використовуючи постійну арифметику, значення трактується буквально в ІЛ.

Пов'язаний момент: Постійне складання застосовується до рядкових літералів. Багато хто вважає, що такий виклик викликає занадто непотрібне, неефективне об'єднання рядків:

public class Program
{
    public static int Main()
    {
        const string a = "a";
        const string b = a + a;
        const string c = "C";
        const string d = "Dee";
        return (a + b + c + d).Length;
    }
}

Але перевірте ІЛ:

IL_0000: nop
IL_0001: ldstr "aaaCDee"
IL_0006: callvirt instance int32 [mscorlib]System.String::get_Length()
IL_000b: stloc.0
IL_000c: br.s IL_000e
IL_000e: ldloc.0
IL_000f: ret

Підсумок: Оператори константних виразів призводять до постійних виразів, а компілятор виконує всі обчислення; це не впливає на продуктивність роботи.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.