C # - Почему префиксы на полях не рекомендуется?


19

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

Для меня, если я читаю чужой код и вижу это:

count = 3;

Я предполагаю, что countэто локальная переменная для этой функции, и я делаю что-то, что будет использоваться в другом месте функции; если я увижу это:

m_count = 3;

Я сразу понимаю, что я обновляю состояние объекта.

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

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

Я счастлив изменить стиль Microsoft, но я бы хотел знать, почему все так, как есть.

Почему сейчас считается плохим иметь возможность определять, является ли переменная локальной функцией или членом?

PS Это очень похоже на то, что в настоящее время считается наилучшей практикой в ​​отношении ключевого слова «это» перед полем и методами в c #? но я спрашиваю m_неthis.

PPS Также см. Почему Microsoft сделала так, чтобы параметры, локальные переменные и частные поля имели одно и то же соглашение об именах?


9
Разве в C # нет thisключевого слова? Не могли бы вы сослаться на пользователей this, использующих , например this.count?
FrustratedWithFormsDesigner

3
Префикс m_ - это C ++, где он избегает конфликтов имен между полями и методами. В C # это не проблема, потому что имена методов должны начинаться с верхнего регистра, а поля с нижнего регистра.
Амон

4
Если вы печатаете код в 2017 году, вы делаете что-то не так. В двух других сценариях должно быть достаточно очевидно, что countони не появляются просто из ниоткуда, поскольку они не были объявлены в методе. Откровенно говоря, аргумент «вне IDE» ДЕЙСТВИТЕЛЬНО слаб. Тем более, что в IDE есть даже инструменты для проверки кода.
Энди

4
Еще актуальный пост от Джоэла .
Кейси

Ответы:


19

Когда они работали над API для Windows, Microsoft рекомендовала использовать венгерскую нотацию, используя «m_», а также «i», «sz» и т. Д. В то время это было полезно, поскольку среда IDE не была устойчивой. достаточно, чтобы показать эту информацию для вас.

C #, с другой стороны, имеет очень надежную IDE с самого начала.

Таким образом, они сделали рекомендацию в Microsoft Naming Guidelines for C # для открытых статических или защищенных полей:

 DO use PascalCasing in field names.
 DO name fields using a noun, noun phrase, or adjective.
X DO NOT use a prefix for field names.

Теперь, когда вы можете навести на него курсор мыши или нажать CTRL + K + W и получить всю информацию об участнике, Microsoft пришла к выводу, что префиксы являются неправильной формой; это ручное решение уже решенной проблемы.

Частные поля специально исключены из руководящих принципов, но Microsoft, похоже, пришла к выводу, что это относится даже к внутренним частным полям, которые можно увидеть, если вы укажете Resharper на .NET 4.5 или .NET Core.

Используйте свои инструменты, не делайте это вручную.


2
Тот же ответ, что и для Энди ... да, но ... во многих ситуациях я смотрю на код за пределами IDE. Примеры - (1) инструменты слияния. (2) инструменты проверки кода. (3) (задыхаясь) распечатки.
Бетти Кроккер

Энди опубликовал буквально тот же момент, что и я. Я увидел всплывающее окно «1 новый комментарий» и нажал «добавить ответ». Что касается этих инструментов, два из них уже есть в IDE. Распечатки ... зачем ты это делаешь?
Кевин

1
Мой первоначальный вопрос остается без ответа ... да, Microsoft сказала, что не используйте m_, и производители инструментов последовали этому, но это не техническая причина для использования m_, это то, что часто называют "призывом к власти". Единственная техническая причина, по которой я не использовал m_, пришла от Тимоти Тракл, и я не понимаю этого ...
Бетти Кроккер,

8
@BettyCrokker Потому что m_ не добавляет абсолютно никакой ценности. IDE говорит вам, член или нет, поэтому m_ является избыточным. Любой, почему ты зациклен на м_? Почему не L_ (для местных)? Почему не afsdfjdskfjas_? Я собираюсь опустить вопрос, потому что это глупый религиозный аргумент в любом случае. Делай что хочешь. Руководящие указания, которые вы стучите, также гласят: «Рекомендации по именованию полей применяются к статическим открытым и защищенным полям . Внутренние и частные поля _не_ не охвачены рекомендациями , а общие или защищенные поля экземпляров не допускаются правилами разработки элементов».
Энди

1
@BettyCrokker Хорошо, но вы спрашиваете о руководящих принципах, которые сделаны с этим предположением. Вы также жалуетесь на статические инструменты, которые, вероятно, также используют это предположение, потому что «никто» (в пределах допустимой статистической границы ошибки) печатает свой код C #. Я не знаю, какие инструменты статистического анализа вы используете, но Resharper довольно специфичен: закрытые поля - это _lowerCase. Открытые члены любого типа или «UpperCase», а локальные - «lowerCase». Экономия на «м» на самом деле выглядит довольно хорошо, в любом случае.
Кевин Фе

17

В C # нет глобальных переменных, поэтому остаются только локальные переменные и поля классов.

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

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

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


Как только вы укажете на него, это также может быть класс или пространство имен.
CodesInChaos

3
Я не думаю, что это действительно отвечает на вопрос.
Владимир Стокич

5
@FrankHileman На самом деле, это так. Это объясняет, почему нет веских причин для унижения имени.
Дедупликатор

2
"uglifying"? Если это так, то это вопрос только мнения. У каждого может быть другое мнение о безобразии. Есть причины для предпочтения одного соглашения другому, но в этом случае стандартного соглашения не существует.
Фрэнк Хайлеман

1
Красота @Deduplicator в глазах смотрящего. Соглашения, которые я когда-то считал некрасивыми, теперь я ценю как полезные.
Фрэнк Хайлеман

11

Почему разработчики избегают добавления пространств имен с помощью директивы using namespace?

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

Нет. Мы ленивые машинистки, но это не то, почему.

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

По той же причине вы не хотите, чтобы в вашей команде было два парня по имени Джон. Тот факт, что у них есть фамилии, не является причиной для присвоения им префиксов или суффиксов. Я просто буду звать тебя Скит. Все остальное - просто боль.


2
Это не похоже на ответ на вопрос. В .net нет общих предпочтений или соглашений для частных полей.
Фрэнк Хайлеман

12
@FrankHileman В общем, мы предпочитаем хорошие имена. Соглашения не создают хороших имен. Соглашения создают такие имена, как McOhPlease Von StopItAlreadyStine.
candied_orange

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

7

Давайте немного расширим.

Существует 3 общих стиля префиксов, каждый из которых иногда встречается в коде c #:

  • m_
  • _
  • это.

Такие префиксы обычно используются в частной реализации класса . Рекомендация Microsoft в основном касается открытых частей класса.

Преимущество использования m_over _заключается в том, что префикс может быть одинаковым во всей вашей кодовой базе, если вы используете c #, а также языки, где использование _в качестве префикса недопустимо . * Преимущество m_over в this.том, что он короче и вы случайно не будете забудь про префикс.

Преимущество _над тем m_, что он короче и что все, что начинается с префикса, сортируется сверху, сортируется в алфавитном порядке с помощью инструмента. Преимущество _над тем this., что он короче, и что вы не забудете случайно о префиксе.

Преимущество this.над m_и в _том, что вы можете использовать его в кодовой базе, где _или m_не являются принятым и универсальным соглашением. Это также означает, что никому не нужно знать о _или m_соглашении, которое можно упростить. Недостатком является то, что, поскольку компилятор не заботится об этом префиксе, this.префикс иногда будет отсутствовать и поэтому не будет столь же надежным, как индикатор.


* Как только вы начнете получать доступ к классам C #, которые используют _из C ++ / CLI (которые действительно не должны использовать _), вы заметите, что согласование общего префикса является более сложным, чем должно быть. Решение не иметь префикса избавляет от этого шума. Объедините это с ответом Дедупликатора, который я перефразирую как «преимущество префикса практически ничтожно», и это дает нам: «При использовании префиксов есть крошечная проблема и крошечный потенциал роста, поэтому это правильный выбор - просто не используй их".


Есть более старый вопрос о стековом потоке, связанный с этим.


1
Хорошее сравнение. Тем не менее, я считаю, что отсутствие префикса хорошо работает на практике.
Phil1970

6

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

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

Вы можете использовать любой стиль, который вам нравится, в ваших личных или локальных переменных.

Сказав, что переменные префиксы вообще не рекомендуется по ряду причин

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

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

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

4
Если вы примените «Они могут ошибаться и, следовательно, вводить в заблуждение» почти ко всему, вы быстро обнаружите, что не следует называть переменные или функции - они могут быть неправильными в конце концов! И я предполагаю, что ваш второй пункт должен сказать "классы, которые вы НЕ создали"? Или иначе я просто не понимаю ... В любом случае, это звучит так, будто наличие единственного префикса подчеркивания для закрытых полей - это неофициальный стандарт, это, вероятно, направление, в котором мы пойдем.
Бетти Кроккер

некоторые вещи проверяются компилятором, поэтому m_count не может быть переменной-членом, но this.count всегда будет
Ewan

многие используют ppl _, но со временем все меняется, вы обнаружите, что код, написанный для "лучшей практики" в прошлом году, не соответствует соглашениям этого года
Ewan

4

Для меня, если я читаю чужой код и вижу это:

count = 3;

Я предполагаю, что 'count' является локальной переменной для этой функции, и я делаю то, что будет использоваться в другом месте в функции

И вы будете абсолютно правы, если вы читаете исходный код, который соблюдает правила стиля C #. В таком коде:

this.count = 3;

присваивает значение полю.

count = 3;

присваивает значение локальной переменной

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

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

public class Hello
{
    private string greetings;

    public Hello(string greetings)
    {
        greetings = greetings; // Wait, what is that?!
    }
}

Разве в C # нет ключевого слова this? Не могли бы вы сослаться на участников, использующих это, таких как this.count?

Да, но (1) это больше печатать, и (2) это легко забыть. Если поле называется m_count, мне не нужно помнить, чтобы ввести this.m_count

Здесь, однако, вы не правы.

Это больше печатать, когда вы пишете свой код в блокноте. С IDE с авто-завершения или, лучше, IntelliSense, сравнение между this.countи m_countне столь очевидна. Обратите внимание на Shiftзнак +, -необходимый для ввода подчеркивания.

Что касается «легко забыть», опять же, это актуально только для пользователей Блокнота. Не только StyleCop и Resharper не позволят вам забыть, что this.нужно делать, когда это необходимо, но после нескольких часов программирования, this.когда это необходимо, это становится привычкой.


6
Я считаю, что использование thisтолько тогда, когда вам нужно, приводит к действительно непоследовательному ощущению и слишком часто отвлекает меня. Если вы собираетесь использовать thisвместо префикса, я считаю, что вы всегда должны использовать его. В этом случае он становится префиксом, и вы также можете использовать _.
RubberDuck

1
RubberDuck прав. "это." это префикс, хотя, он имеет значение компилятора.
Фрэнк Хилман

4

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

можешь объяснить дальше? Ваша единственная причина, по которой я видел, почему не использовать префикс, и я не понимаю его ... (под этим я подразумеваю, что другие люди говорят, что это причины, по которым я не должен его использовать, что не то же самое, что почему я не должен) - Бетти Кроккер

Я хочу расширить ответы @CandiedOrange и @Ewan.

Префиксы усложняют рефакторинг

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

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

class TaxCalculator{
   double Calculate(double p_TaxRateInpercent, double p_BaseValue){
     return (100+p_TaxRateInpercent)/100 * p_BaseValue;
   }
} 

Затем вы должны выполнить обратную операцию, и в соответствии с TDD вы делаете это с наименьшими усилиями:

class TaxCalculator{
   double Calculate(double p_TaxRateInpercent, double p_BaseValue){
     return (100+p_TaxRateInpercent)/100 * p_BaseValue;
   }
   double CalculateBase(double p_TaxRateInpercent, double p_TaxedValue){
     return p_TaxedValue/((100+p_TaxRateInpercent)/100);
   }
} 

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

class TaxCalculator{
   double m_TaxRateInpercent;
   TaxCalculator(double p_TaxRateInpercent){
     m_TaxRateInpercent = p_TaxRateInpercent;
   }
   double Calculate(double p_BaseValue){
     return (100+m_TaxRateInpercent)/100 * p_BaseValue;
   }
   double CalculateBase(double p_TaxedValue){
     return p_TaxedValue/((100+m_TaxRateInpercent)/100);
   }
} 

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

Проблема в том, что у вас нет поддержки со стороны вашей IDE для переименования по всему классу. Единственная помощь это поиск / замена, которая также изменит конструктор или любую часть, которая случайно содержит строку p_TaxRateInpercent.
Ваша IDE может предложить изменение ... рефакторинга для неопределенных имен переменных, но это может быть ограничено областью применения метода

Без префикса изменились бы только сигнатуры метода. Переименование не понадобилось бы вообще.


Префиксы загромождают историю SCM при изменении

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


1

Я использую префикс _ для всех переменных-членов. Я ненавижу префикс «this», потому что, хотя некоторые утверждают, что это правильный способ работы - он добавляет много шума / беспорядка в вашу кодовую базу. Отдельное подчеркивание также является обычной практикой в ​​примерах Microsoft.

Так что в вашем примере я бы имел:

int _count = 10;
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.