Является ли использование имен параметров, которые отличаются от имен типов только регистром, плохой практикой в ​​C #?

43

Я вижу подобные вопросы в отношении имен параметров, которые соответствуют свойствам класса, но я не могу найти ничего относительно использования имени параметра, совпадающего с именем типа параметра, за исключением случая в C #. Кажется, это не нарушение, которое я могу найти, но считается ли это плохой практикой? Например, у меня есть следующий метод

public Range PadRange(Range range) {}

Этот метод берет диапазон и возвращает новый диапазон, к которому применен некоторый отступ. Итак, учитывая общий контекст, я не могу придумать более описательное имя для параметра. Однако мне напомнили один совет, который я прочитал, читая Code Complete о «психологической дистанции». Это говорит

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

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

public Range PadRange(Range myRange) {}

Я лично испытываю сильное отвращение к этой конвенции. Добавление префикса «my» к именам переменных не дает дополнительного контекста.

Я также вижу следующее

public Range PadRange(Range rangeToPad) {}

Мне нравится это лучше, чем «мой» префикс, но все равно мне все равно. Мне это кажется слишком многословным и неуклюже читается как имя переменной. Для меня понятно, что диапазон будет дополнен из-за имени метода.

Так что со всем этим изложено, моя интуиция должна пойти с первой подписью. Для меня это чисто. Не нужно форсировать контекст, когда он не нужен. Но я делаю себе или будущим разработчикам медвежью услугу с этим соглашением? Я нарушаю лучшую практику?

Джейсон Тайлер
источник
35
Если вы не можете придумать более описательный параметр, Range rangeхорошо.
Роберт Харви
21
В этой ситуации среда IDE будет по-разному окрашивать «Range» и «Range», поэтому очень легко увидеть, что один является типом, а другой - именем переменной.
17 из 26
10
Да, я помню это. Это в их руководящих принципах проектирования в качестве рассмотрения. Msgstr "РАССМОТРЕТЬ, давая свойству то же имя, что и его тип." docs.microsoft.com/en-us/dotnet/standard/design-guidelines/…
Джейсон Тайлер
11
Также хорошо: Range r(по крайней мере, для короткого тела метода) и Range toPad.
Берги
19
Я всегда рассматривал префикс «мой» как нечто, что делается в примере кода, где он используется, чтобы указать читателю, что имя должно быть изменено на что-то другое в зависимости от контекста. Это не то, что на самом деле должно заканчиваться в любом реальном коде.
Kapex

Ответы:

100

Не думай об этом, Range rangeхорошо. Я использую такое наименование уже более 15 лет в C # и, вероятно, гораздо дольше в C ++, и никогда не испытывало каких-либо реальных недостатков, как раз наоборот.

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

Док Браун
источник
6
Еще одно исключение, которое я бы добавил, - это когда для экземпляра выполняется определенное действие. Есть немного места для того, чтобы быть более наглядным, например, зачем нужен этот параметр. Wack (Bozo bozoToBeWacked) Тем более, если параметр является необязательным и описывает, что означает объект в этом сценарии / на что он будет влиять. Мое эмпирическое правило, если не глядя без кода на то, что делает параметр, ему нужно лучшее имя и / или комментарии.
Майк
17
Я бы рассмотрел Wack(Bozo bozoToBeWacked)в качестве примера избыточность в имени переменной, которая достаточно выражена самим методом (и, следовательно, нежелательна для исходного вопроса). Wack(Person bozo)однако, является более ценным с точки зрения семантики, так как подразумевает, что ожидается уничтожение только бозо.
Мирал
2
В случае, если вы упомянули во втором абзаце, я часто буду переименовывать параметр в нечто подобное initialRange. Это все еще очень общий характер, но не имеет почти такой же уровень отвращения, как myRange.
Jaquez
@Miral: это становится более актуальным в таких случаях, как Punch(Bozo punchingBozo, Bozo bozoToBePunched). Подробное именование более уместно, когда вам нужно различать несколько вещей (которые семантически описываются практически одинаковыми словами). Предложение OP Range rangeToPadне обязательно в его примере метода с одним параметром, но может быть крайне необходимо для метода, который принимает несколько Rangeобъектов.
Флейтер
7
@Flater Punch(Bozo source, Bozo target)сделает работу
Томас Аюб
17

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

this.range = range;

И у меня обычно есть свойство с именем Range.

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

Вы должны быть строгими с полностью квалифицированными членами с «этим». хотя, но для этого и нужен StyleCop.

Примечание к стилю

Споры гарантированы!

Тем, кто выступает против использования «этого»: я видел _, m, m_ и просто ничего. Сам язык предлагает нам совершенно ясный, однозначный, общепризнанный способ указать, что мы имеем дело с учеником. С какой стати вы хотите придумать свой собственный путь, который искажает уже совершенные имена?

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

«Больше персонажей!» Шутки в сторону? "Время компиляции взлетит до небес!" Шутки в сторону? «Мне придется поднять мой мизинец при его наборе!». Как будто время набора текста имело какое-либо значение в общем времени разработки.

Я признаю, что любой стиль, отличный от того, к которому вы привыкли, вызовет некоторую оппозицию. Но с этим постоянно трудно спорить. Вот как это работает для меня: прежде чем я добавляю новый файл кода, я запускаю StyleCop, и он обнаружит, что в ряде участников отсутствуют квалификаторы «this». Я поставил «это». в буфер обмена, запустите его и вставьте. Никаких усилий.

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

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

Мартин Маат
источник
19
« Вы должны быть строгими с полностью квалифицированными членами с« этим », хотя, « -1. Не используйте, thisкроме случаев, когда это необходимо. Это просто шум в противном случае. Используйте _rangeдля поля и thisникогда не требуется, кроме как в методах расширения.
Дэвид Арно,
12
Я согласен с @DavidArno. «это» просто чистый шум. «Range» - это свойство, «_range» - это поле, а «range» - это параметр.
17 из 26
8
@David Если переменная является членом класса, она СУЩЕСТВЕННА и бьет любой произвольный префикс, чтобы сигнализировать, что вы смотрите на члена класса, а не на аргумент или локальную переменную.
Мартин Маат
6
Моя IDE будет стрелять огненными шарами, как только я назначу переменную себе.
Koekje
21
@DavidArno Хотите объяснить, почему «шум» _предпочтительнее шума this.? Я бы сказал, что один из них имеет значение, обеспечиваемое языком (и обычно имеет подсветку синтаксиса), а другой - нет.
Клинт
9

Мое руководство по именованию методов, параметров и переменных довольно простое:

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

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

Range Pad(Range toPad)

Сокращение имени метода говорит само за себя.

Имя параметра toPadнемедленно сообщает читателю, что этот параметр, вероятно, будет изменен на месте путем дополнения, а затем возврата. Напротив, нельзя делать никаких предположений о названной переменной range.

Кроме того, в фактическом теле метода любые другие Rangeвводимые переменные будут (должны) называться по их намерению, так что вы можете иметь paddedи unpadded... toPadсоответствовать этим соглашениям об именах, но rangeпросто выпячиваться и не склеиваться.

Ян Кемп
источник
3
padded/ unpaddedзвучит хорошо для локальных неизменяемых переменных. Но если есть изменяемый toPadпараметр, то после того, как заполнение выполнено, имя toPadбольше не подходит (если результат не возвращается немедленно). Я бы предпочел придерживаться Range rangeв этих случаях.
Kapex
@ Kapep Я бы назвал это просто result. Он модифицируется и возвращается . Последнее ясно из названия, и первое следует, поскольку возвращение неизмененного аргумента не имеет смысла.
Maaartinus
Из подписи Padметод возвращает a Range. Для меня это означает, что тот, который я передал, будет сохранен, не изменен на месте, и Rangeбудет возвращен новый, дополненный . ,
Аарон Эшбах
1
Я не уверен, что думать о вашем совете. Pad(toPad)чувствует себя как-то не так ИМХО. Вы делаете хорошую точку зрения, отстаивая свою точку зрения, хотя. Вы получаете +0 от меня! :)
Эрик Думинил
В вопросе говорится, что он «возвращает новый диапазон, к которому применен некоторый отступ». Я согласен с тем, что вы называете сценарий таким, как вы думаете, но для реального вопроса я бы выбрал что-то вроде Range CreateRangeWithPadding (Range source)
Ричард Уиллис,
3

Для именования элементов кода (типов, переменных, функций, чего угодно), ключевой вопрос, который нужно задать себе:

Если я сделаю опечатку, компилятор найдет ее для меня?

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

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

Если вас интересует, будут ли две переменные, методы, функции или свойства в текущей области вызваны rangeи Range. В этом случае компилятор, вероятно , пропустит его, и вы получите неожиданное поведение во время выполнения. Обратите внимание , что это два из любых из этих типов коды элемента, а не только «две переменных» или «две функций» , - все это может быть неявно друг к другу, в результате чего с бойней , когда он работает. Вы можете получать предупреждения, но вы не можете гарантировать ничего большего. У вас есть похожие проблемы, если у вас есть два типа, объявленные как rangeи Range.

Также обратите внимание, что то же самое относится и к венгерскому стилю обозначений , где имена имеют префикс с одним или несколькими символами, чтобы сказать что-то еще о чем бы то ни было. Если у вас есть переменная с именем Rangeи указатель на нее PRange, вы легко можете P, например, случайно пропустить . C # должен поймать это, но C и C ++ будут давать только самое большее предупреждение. Или, что еще более тревожно, предположим, что у вас есть двойная версия, DRangeи вы уменьшите ее до плавающей версии FRange. Используйте поплавок один случайно (что легко , так как ключи находятся рядом на клавиатуре) и ваш код будет вид работы, но она будет падать в странных и непредсказуемо , когда процесс заканчивается разрешением и потери значимости.

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

Грэхем
источник
Просто примечание для читателей - для C # люди действительно должны избегать венгерской нотации. Хотя это было полезно в старые времена, когда выделение синтаксиса не было чем-то особенным или было ограниченным, в настоящее время это действительно не помогает.
Т. Сар - Восстановить Монику
@ T.Sar Даже в те дни я ненавидел это со страстью! Как вы говорите, лучшие IDE решили проблемы, на которые она была направлена, но это все еще появляется время от времени. Вы все равно увидите это, если вам понадобится поговорить с Windows API, например, по историческим причинам. Я не хотел полностью разбивать предпочтительные стили людей, если им это действительно нравится, но я хотел использовать их как ловушку, чтобы показать, что прописные / строчные буквы - не единственный способ испортить наименование.
Грэм,
Я знаю, что вы не предлагаете использовать венгерскую нотацию, но я также знаю, что молодые разработчики имеют огромную слабость к вещам с клевыми именами. Возможность сказать: «Мой код написан в этом сверхсекретном стиле кодирования ниндзя - венгерская нотация!» может звучать непреодолимо круто для младших умов. Я видел слишком много разработчиков, ставших жертвами шумихи из холодных слов ... Венгерская нотация - это боль, придающая форму исходного кода xX
T. Sar - Reinstate Monica
@ T.Sar Правда, достаточно. «Те, кто не помнит прошлого, обречены его повторять» и все такое. : /
Грэм
1
Говорим ли мы об оригинальной венгерской нотации или о том, как она была грубо неверно истолкована Microsoft (рядом с «авторами документации в команде Windows непреднамеренно придумано то, что стало известно как Systems Hungarian», а также в интервью Джоэлю Спольски Лео Лапорте по эпизоду триангуляции 277, 2016-12-12, 04 минуты 00 секунд - 06 минут 35 секунд )?
Питер Мортенсен
1

Я хотел бы добавить один анекдот, хотя Range rangeэто синтаксически допустимо, но это может усложнить отладку или рефакторинг. Ищете эту переменную с именем «диапазон» в файле с большим количеством переменных типа Range? Вы можете в конечном итоге выполнить больше работы в результате этого выбора имен.

Это в значительной степени зависит от контекста, хотя. Если это файл из 30 строк, мои утверждения не вступают в игру.

Джейкоб М.
источник
7
Большинство людей используют инструменты, которые различают типы и параметры или переменные, для разработки Windows. То, что вы описываете, напоминает мне о старых добрых grep-днях в Unix.
Фрэнк Хайлеман
1
@FrankHileman Это может вас удивить, но Unix все еще жив, а grep все еще используется (: D). Поскольку grep по умолчанию чувствителен к регистру, упомянутой проблемы просто не существует. Тем не менее, даже мы, зависимые от терминала ненавистники окон, редко используем grep для исходного кода, так как IDE намного лучше при обычном поиске.
Maaartinus
Я думаю, что мы согласны с тем, что платформа не в этом. grepэто отличный инструмент, но это не инструмент рефакторинга. И инструменты рефакторинга существуют на каждой платформе разработки, которую я использовал. (OS X, Ubuntu, Windows).
Эрик Уилсон,
@EricWilson Когда-то grep и sed были единственными инструментами рефакторинга в Unix.
Фрэнк Хайлеман
@FrankHileman То, что что-то используется в качестве инструмента рефакторинга, не означает, что оно есть. Я могу рефакторинг в блокноте, но это не больше, чем текстовый редактор.
Эрик Уилсон,
1

Я думаю, что вы можете использовать в Range rangeнастоящее время по одной причине: подсветка синтаксиса. Современные IDE обычно выделяют имена типов и имена параметров разными цветами . Кроме того, тип и переменная имеют значительную «логическую дистанцию», которую легко перепутать.

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

akaltar
источник
0

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

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

public String removeNonDigits(String phoneNumber)

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

Так что, вместо того, чтобы сказать Range range, вы можете сказать Range rangeToPad. Но какую информацию это добавляет? Конечно, это диапазон для колодки. Что еще это будет?

Добавление некоторого произвольного префикса, «my» или «m_» или чего-либо еще, передает нулевую дополнительную информацию читателю. Когда я использовал языки, в которых компилятор не допускает, чтобы имя переменной совпадало с именем типа - с учетом или без учета регистра - я иногда ставил префикс или суффикс, просто чтобы получить его для компиляции , Но это только для удовлетворения компилятора. Можно утверждать, что даже если компилятор может различать, это облегчает распознавание читателю. Но вау, в Java я написал такие выражения, как «Customer customer = new Customer ();» миллиард раз, и я никогда не находил это запутанным. (Я всегда считал это немного избыточным, и мне нравится, что в VB вы можете просто сказать «dim customer as new Customer», и вам не нужно давать имя класса дважды).

Я категорически возражаю против общих имен, когда в одной и той же функции есть два или более экземпляров одного типа. ОСОБЕННО параметры. Подобно:

public Range pad(Range range1, Range range2)

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

public boolean overlap(Range range1, Range range2)

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

Но если они разные, дайте мне понять, чем они отличаются! Я недавно работал над программой, в которой был класс «Place» для хранения данных о географических местах, и с переменными этого типа с именами «p», «place», «place2», «myPlace» и т. Д. имена действительно помогают мне определить, что есть что.

сойка
источник