Это правильная ситуация, чтобы использовать константу?


42

Так что мой профессор отзывался о проекте, над которым я работал. Он установил несколько отметок для этого кода:

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

Это в выпадающем списке «обработчик изменения индекса». Он используется, когда пользователь хочет создать нового поставщика, моя опция top (индекс 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: Вы уверены, что у вас нет реакции коленного рефлекса здесь? Это правда, что такие вещи, как -1in str.indexOf(substr) != -1для « strсодержит substr», полностью оправдано. Но здесь значение 0 не является ни очевидным (каково отношение к созданию нового поставщика?), Ни истинно постоянным (что, если способ создания нового поставщика изменяется?).

62
Вы должны выучить правила, прежде чем нарушать правила
Ryathal

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

14
Вы можете использовать int.Zeroвместо этого, чтобы сделать его счастливым :)
Пол Stovell

Ответы:


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
Это все еще неправильно. Это переносит проблему с целочисленного литерала (нуля) на строковый литерал (Create New Vendor). В лучшем случае проблема заключается в том, что «если метка изменится», а не «что если индекс изменится».
Freiheit

7
@Freiheit: Неверно. Строковая константа здесь только для отображения; это никак не влияет на логику программы. В отличие от использования магических значений / строк для хранения состояния программы, изменение этой строки (или добавление / удаление объектов из списка) никогда не может сломать ничего.
BlueRaja - Дэнни Пфлюгофт

83

Порядок в поле со списком может измениться. Что, если вы добавите еще один параметр, например «Создать специального поставщика ...», перед вашим «Создать нового продавца ...»

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

Использование константы также более читабельно, чем литерал.

if (comboVendor.SelectedIndex == NewVendorIndex)

Большинство скомпилированных языков подставляют константу во время компиляции, поэтому нет снижения производительности.


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

1
@ Я согласен, идентификация элементов управления по порядку в графическом интерфейсе очень хрупкая. Если язык поддерживает добавление значения в элемент gui, который можно искать, я бы использовал это.
Майкл Круссел

3
@ решение, потому что это соглашение.
Ikke

3
@Ikke Это определенно не конвенция. stackoverflow.com/questions/242534/…
SolutionYogi

1
Если мы говорим о C #, то NewVendorIndex - это соглашение. Это соответствует остальному стилю .NET.
MaR

36

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

Реальный ответ - то, что он выбрал это, чтобы преподать вам урок.

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

Я бы сказал, что он неплохо справляется.

Конечно, он может показаться немного абсолютным, но я уверен, что вы подумаете еще раз, прежде чем использовать магические числа.

Кроме того, он попал под вашу шкуру, чтобы вы могли присоединиться к онлайн-сообществу программистов, чтобы узнать, что считается наилучшей практикой в ​​этой ситуации.

Снимаю шляпу перед вашим профессором.


+1 за указание на то, что в этом случае средства оправдывают конечный результат
Оливер-Клэр

@ Танос: « Не забывай, что он профессор, его работа - учить тебя программированию и передовым методам». Это никогда не было так для моего профессора. Я бы сказал, что его работа заключается в том, чтобы научить вас тому, что департамент считает важным после создания курса.
Ramhound

13

[...] моя верхняя опция (индекс 0, которая никогда не меняется) открывает диалоговое окно «Создать нового поставщика».

Тот факт, что вам пришлось объяснить, доказывает, почему вы должны использовать константу. Если вы введете константу как NEW_VENDOR_DIALOG, ваш код будет более понятным. Кроме того, компиляторы оптимизируют константы, поэтому производительность не изменится.

Пишите программы для программистов, а не для компиляторов. Если вы специально не пытаетесь микрооптимизировать, что не похоже на вас.


2
-1 даже за упоминание производительности. Даже если компьютер не оптимизирован, он может выполнять миллиарды таких операций, прежде чем пользователь заметит.
Борис Янков

3
@Boris OP, казалось, был обеспокоен пустой тратой ресурсов, поэтому я упомянул об этом. Я не понимаю, как из-за этого мой ответ будет менее правильным.
kba

12

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

Я бы согласился Использование нуля здесь "магия". Представьте, что вы читаете этот код впервые. Вы не знаете, почему ноль особенный, а литерал ничего не говорит вам о том, почему ноль особенный. Если вместо этого вы сказали, if(comboVendor.SelectedIndex == CreateNewVendorIndex)то начинающему читателю становится совершенно ясно, что означает код.

Он утверждает, что я не должен использовать литералы в моем коде вообще.

Это крайняя позиция; реалистичной позицией было бы сказать, что использование литералов - это красный флаг, который указывает, что код может быть не таким ясным, как мог бы быть. Иногда это уместно.

Дело в том, что я не понимаю, почему я хотел бы сделать этот код в такой ситуации постоянным. Этот индекс никогда не изменится

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

и это не то, что вам нужно настроить.

В самом деле? Вы не можете увидеть любую ситуацию , в которой кто - то может потребоваться изменить порядок вещей в поле со списком?

Тот факт, что вы можете увидеть причину, по которой это может измениться в будущем, является хорошей причиной, чтобы не делать ее постоянной. Скорее это должно быть непостоянное целочисленное поле только для чтения. Константа должна быть величиной, которая гарантированно останется неизменной на все времена . Пи и атомный номер золота являются хорошими константами. Номера версий не являются; они меняют каждую версию. Цена на золото, очевидно, ужасная константа; это меняется каждую секунду. Делайте только постоянные вещи, которые никогда не изменятся .

Кажется пустой тратой памяти хранить один 0 в памяти, который используется для очень специфической ситуации и никогда не меняется.

Теперь мы подошли к сути вопроса.

Это, пожалуй, самая важная строка в вашем вопросе, поскольку она указывает на то, что у вас есть какое-то глубоко ошибочное понимание (1) памяти и (2) оптимизации. Вы в школе, чтобы учиться, и сейчас самое время получить правильное понимание основ. Можете ли вы объяснить подробно, почему вы считаете, что «держать в памяти один ноль»? Прежде всего, почему вы считаете, что оптимизация использования четырех байтов памяти в процессе с как минимум двумя миллиардами байтов адресуемой пользователем памяти имеет значение? Во-вторых, какой именно ресурс вы потребляете здесь ? Что вы подразумеваете под "памятью"?

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


6

Он прав. Вы правы. Ты не прав.

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

Как говорится, вы правы. В этом конкретном случае я действительно не думаю, что константа оправдана. Вы ищете первый элемент в списке, который всегда равен нулю. Это никогда не будет 23. Или -pi. Вы специально ищете ноль. Я действительно не думаю, что вам нужно загромождать код, делая его константой.

Однако вы ошибаетесь, предполагая, что константа переносится как переменная, «используя память». Постоянная существует для человека и компилятора. Он говорит компилятору поместить это значение в это место во время компиляции, где вы в противном случае поместили бы буквальное число. И даже если бы он содержал постоянную в памяти для всех приложений, кроме самых требовательных, потеря эффективности даже не поддалась измерению. Беспокойство об использовании в памяти одного целого числа определенно попадает в «преждевременную оптимизацию».


1
Я бы почти проголосовал за этот ответ, если бы не третий абзац, утверждая, что использование литерала 0 достаточно ясно и никакой константы не требуется. Гораздо лучшим решением было бы вовсе не полагаться на индексацию элемента комбинированного списка, а вместо этого на значение выбранного элемента. Магические константы - это магические константы, даже если они равны 0 или 3,14 или еще чего-то подобного - назовите их соответствующим образом, так как это делает код более читабельным.
Роланд Тепп

Может быть, лучше использовать значение в списке, но это был не тот вопрос, о котором он говорил. Его вопрос был о том, было ли это подходящим использованием константы. И если сравнивать с 0 в контексте взаимодействия с GUI (по какой-либо причине - возможно, кто-то ищет первый элемент независимо от значения), я думаю, что в этом месте нет необходимости использовать константу.
GrandmasterB

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

2

Я бы заменил на 0константу, чтобы прояснить смысл, например NewVendorIndex. Вы никогда не знаете, изменится ли ваш заказ.


1

Это полное предпочтение вашего профессора. Как правило, вы используете константу только в том случае, если литерал будет использоваться несколько раз, вы хотите, чтобы читатель понял, какова цель строки, или ваш литерал может быть изменен в будущем, и вы хотите изменить только это в одном месте. Тем не менее, в этом семестре профессор является начальником, так что я бы сделал это с этого момента в этом классе.

Хорошее обучение для корпоративного мира? Вполне возможно.


2
Я не согласен с частью «использовать только константу, если литерал будет использоваться несколько раз». С 0 (и, возможно, -1, 1) это часто понятно, в большинстве случаев лучше дать имя вещи, которая намного понятнее при чтении кода.
Йоханнес

1

Честно говоря, хотя я не думаю, что ваш код - лучшая практика, его предложение немного откровенно гротескно.

Более распространенной практикой для комбинированного списка .NET является присвоение элементу «Выбрать ..» пустого значения, в то время как фактические элементы имеют значимые значения, а затем выполните:

if (string.IsNullOrEmpty(comboVendor.SelectedValue))

скорее, чем

if (comboVendor.SelectedIndex == 0)

3
В вашем примере, null - это просто еще один литерал. Суть этого урока в том, что нужно избегать букв.
промахнулся

@ overslacked - в моем примере нет нулевого литерала.
Carson63000

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

Carson63000 - я имел в виду ваше использование IsNullOrEmpty. Вы меняете одну «магическую ценность» на другую. @Ramhound - Null - приемлемый литерал в некоторых случаях, без сомнения, но я не верю, что это хороший пример (использование null или пробела в качестве магического значения).
промахнулся

-1 за "немного гротеск". Хотя вы правы в том, что использование значения было бы более общепринятым и более простым для понимания, при использовании selectedindex использование именованной константы определенно лучше, чем просто 0.
jmoreno

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 #, как и в большинстве языков, константы «сворачиваются» компилятором. Например, скомпилируйте следующую программу и проверьте IL. Вы обнаружите, что все эти цифры даже не попадают в IL, не говоря уже о памяти компьютера:

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);
    }
}

результирующий IL:

.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
}

Таким образом, независимо от того, используете ли вы константу, литерал или килобайт кода с использованием константы, значение обрабатывается буквально в IL.

Связанный пункт: постоянное сворачивание применяется к строковым литералам. Многие считают, что такой вызов вызывает слишком много ненужной неэффективной конкатенации строк:

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:

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.