Соглашение об именах - подчеркивание в переменных C ++ и C #


146

Распространено видеть _varимя переменной в поле класса. Что означает подчеркивание? Есть ли ссылка на все эти специальные соглашения об именах?



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

26
@Mike - Ваше общее утверждение просто не соответствует действительности. Я обнаружил, что использование этого соглашения значительно облегчает быстрое сканирование методов и хорошее понимание класса.
ChaosPandion

12
@ChaosPandion: тогда не читайте это как общее утверждение. «Некоторые» не означают «все», а «слишком часто» не означают «всегда», и, возможно, вы исключение из моего опыта.
Майк Сеймур

17
В C ++ избегайте начальных подчеркиваний. Во многих контекстах (т. Е. В глобальном масштабе, когда заглавная буква следует и т. Д.), Они зарезервированы для реализации, и вы фактически рискуете получить некоторую макрополоску над ними. Так что, если вы хотите их, сделайте их подчеркивания .
2010 года

Ответы:


120

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

В C ++ подчеркивание обычно указывает на закрытую переменную-член.

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

Перед:

private string _name;
public string Name
{
    get { return this._name; }
    set { this._name = value; }
}

После:

public string Name { get; set; }

11
За исключением команд, которые хотят, чтобы их свойства были неизменными.
ChaosPandion

11
Идентификаторы, начинающиеся с подчеркивания, зарезервированы для компилятора. Использование префиксов подчеркивания может конфликтовать с символами компилятора.
Томас Мэтьюз

12
@ Томас Мэтьюз - не в C #.
EMP

11
@ChaosPandion Я предполагаю, что то, что вы подразумеваете под «неизменяемыми», на самом деле «только для чтения», и в этом случае можно использовать public string Name { get; private set; }. Правда, он не совсем неизменен, но он есть.
jdmichal

7
@Thomas В контексте класса идентификаторы начинаются с подчеркивания, за которым следует заглавная буква, зарезервированная в C ++. _varне зарезервировано
Тайлер МакГенри

84

Рекомендуется НЕ использовать UNDERSCORES перед любым именем переменной или параметра в C ++

Имена, начинающиеся с подчеркивания или двойного подчеркивания, ЗАБРОНИРОВАНЫ для разработчиков C ++. Имена с подчеркиванием зарезервированы для работы библиотеки.

Если вы читали в стандарте кодирования C ++, вы увидите, что на самой первой странице написано:

«Не переусердствуйте в присвоении имен, но используйте последовательное соглашение об именах: есть только два обязательных элемента: а) никогда не используйте« закулисные имена », те, которые начинаются с подчеркивания или содержат двойное подчеркивание;» (p2, Стандарты кодирования C ++, Херб Саттер и Андрей Александреску)

В частности, рабочий проект ISO устанавливает действующие правила:

Кроме того, некоторые идентификаторы зарезервированы для использования реализациями C ++ и не должны использоваться иначе; Диагностика не требуется. (a) Каждый идентификатор, который содержит двойное подчеркивание __ или начинается с подчеркивания, за которым следует заглавная буква, зарезервирован для реализации для любого использования. (b) Каждый идентификатор, который начинается со знака подчеркивания, зарезервирован для реализации для использования в качестве имени в глобальном пространстве имен.

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

Вы сами можете понять, почему такое использование подчеркивания может иметь катастрофические последствия при разработке программного обеспечения:

Попробуйте скомпилировать простую программу helloWorld.cpp, например:

g++ -E helloWorld.cpp

Вы увидите все, что происходит в фоновом режиме. Вот фрагмент:

   ios_base::iostate __err = ios_base::iostate(ios_base::goodbit);
   try
     {
       __streambuf_type* __sb = this->rdbuf();
       if (__sb)
  {
    if (__sb->pubsync() == -1)
      __err |= ios_base::badbit;
    else
      __ret = 0;
  }

Вы можете увидеть, сколько имен начинаются с двойного подчеркивания!

Также, если вы посмотрите на виртуальные функции-члены, вы увидите, что * _vptr - это указатель, сгенерированный для виртуальной таблицы, который автоматически создается, когда вы используете одну или несколько виртуальных функций-членов в своем классе! Но это другая история ...

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


4
Разве имена, которые не являются глобальными или имеют файловую область и не начинаются с символа подчеркивания и строчной буквы, совершенно не подходят? Я делаю это все время, потому что это очень чисто для конкретных случаев, например: void A :: set_size (int _size) {size = _size; }. Что вы делаете для тривиального использования, как это?
тукра

3
Подчеркнутое, сопровождаемое строчной буквой, разрешено в неглобальной / файловой области, я полагаю. Можете ли вы показать мне, где в стандарте написано, что это не так? Проблема с последним подчеркиванием в том, что я уже иногда использую это для членов. Так что я мог бы иметь функцию 'int size ();' член var 'int size_;' и аргумент 'int _size'. Это имеет тенденцию хорошо освещать вещи, и я не видел лучших альтернатив. Функции с заглавными буквами невозможны для дженериков, работающих с STL. Стиль 'this.size', который нравится людям в C #, кажется мне подверженным ошибкам, если только вы не всегда используете его для доступа к членам, что ужасно.
тукра

7
Я понимаю, что некоторые люди не хотят использовать подчеркивание и строчные буквы, потому что у них могут возникнуть проблемы с тем, чтобы понять, что является законным. Стандарт C ++ допускает это, даже если некоторые авторы рекомендуют его не использовать. Для меня, поскольку это полностью законно и не зарезервировано, я не вижу причин считать это рискованным. Спасибо за ваш отзыв.
Тукра

3
@tukra: Это не полностью легальное. Стандарт C ++ только позволяет вам использовать один символ подчеркивания , за которым следует строчной буквы в локальной области видимости. Удачи! :-)
Константинос Глинос

4
Сожалею. Я думал, что нам уже не нужно повторять квалификации. Идентификатор C ++, начинающийся со знака подчеркивания, за которым следует буква в нижнем регистре, разрешен в любой области, кроме области файла. Это безопасно и законно в функциях, прототипах функций, классах, структурах, объединениях, перечислениях и всем, что вы помещаете в пространство имен. Если вы следуете обычной практике размещения всего своего кода в пространствах имен, тогда вы в полной безопасности.
тукра

45

На самом деле _varсоглашение происходит от VB, а не C # или C ++ (m _, ... это другое дело).

Это привело к преодолению нечувствительности к регистру VB при объявлении свойств.

Например, такой код не возможен в VB, потому что он считает userи Userтем же идентификатором

Private user As String

Public Property User As String
  Get
    Return user
  End Get
  Set(ByVal Value As String)
    user = value
  End Set
End Property

Таким образом, чтобы преодолеть это, некоторые использовали соглашение, чтобы добавить '_' в приватное поле, чтобы получить вот так

Private _user As String

Public Property User As String
  Get
    Return _user
  End Get
  Set(ByVal Value As String)
    _user = value
  End Set
End Property

Поскольку многие соглашения предназначены для .Net и для сохранения некоторого единообразия между соглашениями C # и VB.NET, они используют один и тот же.

Я нашел ссылку на то, что я говорил: http://10rem.net/articles/net-naming-conventions-and-programming-standards---best-practices

Чехол для верблюдов с ведущим символом подчеркивания. В VB.NET всегда указывайте «Защищенный» или «Частный», не используйте «Тусклый». Использование «m_» не рекомендуется, так же как и использование имени переменной, которое отличается от свойства только регистром, особенно с защищенными переменными, поскольку это нарушает соответствие, и сделает вашу жизнь болезненной, если вы будете программировать на VB.NET, так как вы придется называть ваших членов чем-то отличным от свойств аксессора / мутатора. Из всех перечисленных здесь подчеркивание является единственным спорным. Лично я предпочитаю это вместо прямого символа верблюда для моих личных переменных, чтобы мне не приходилось квалифицировать имена переменных с помощью «this». чтобы отличить от параметров в конструкторах или в других местах, где у меня, вероятно, будет конфликт имен. С учетом нечувствительности к регистру в VB.NET это еще более важно, поскольку ваши свойства аксессора обычно имеют то же имя, что и ваши закрытые переменные-члены, за исключением подчеркивания. Что касается M_, это действительно только эстетика. Я (и многие другие) нахожу безобразным, так как в имени переменной есть пробел. Это почти оскорбительно. Я использовал его в VB6 все время, но это только потому, что переменные не могли иметь подчеркивания в начале. Я не мог быть счастливее видеть, как это уходит. Microsoft рекомендует против m_ (и прямой _), хотя они и сделали в своем коде. Кроме того, префикс с прямой "м" прямо. Конечно, поскольку они кодируют в основном на C #, у них могут быть закрытые члены, которые отличаются только в случае от свойств. VB люди должны сделать что-то еще. Вместо того, чтобы пытаться придумывать языковые особые случаи, я рекомендую подчеркивание для всех языков, которые его поддерживают. Если я хочу, чтобы мой класс был полностью CLS-совместимым, я мог бы опустить префикс для любых переменных-членов, защищенных в C #. На практике, однако, я никогда не беспокоюсь об этом, так как я держу все потенциально защищенные переменные-члены в секрете и вместо этого предоставляю защищенные методы доступа и мутаторы. Почему: в двух словах, это соглашение простое (один символ), легко читаемое (ваш взгляд не отвлекается на другие ведущие символы) и успешно избегает конфликтов имен с переменными уровня процедуры и свойствами уровня класса. Свойства уровня класса , Я рекомендую нижнее подчеркивание для всех языков, которые его поддерживают. Если я хочу, чтобы мой класс был полностью CLS-совместимым, я мог бы опустить префикс для любых переменных-членов, защищенных в C #. На практике, однако, я никогда не беспокоюсь об этом, так как я держу все потенциально защищенные переменные-члены в секрете и вместо этого предоставляю защищенные методы доступа и мутаторы. Почему: в двух словах, это соглашение простое (один символ), легко читаемое (ваш взгляд не отвлекается на другие ведущие символы) и успешно избегает конфликтов имен с переменными уровня процедуры и свойствами уровня класса. Свойства уровня класса , Я рекомендую нижнее подчеркивание для всех языков, которые его поддерживают. Если я хочу, чтобы мой класс был полностью CLS-совместимым, я мог бы опустить префикс для любых переменных-членов, защищенных в C #. На практике, однако, я никогда не беспокоюсь об этом, так как я держу все потенциально защищенные переменные-члены в секрете и вместо этого предоставляю защищенные методы доступа и мутаторы. Почему: в двух словах, это соглашение простое (один символ), легко читаемое (ваш взгляд не отвлекается на другие ведущие символы) и успешно избегает конфликтов имен с переменными уровня процедуры и свойствами уровня класса. Свойства уровня класса , Я никогда не беспокоюсь об этом, так как я держу все потенциально защищенные переменные-члены в секрете и вместо этого поставляю защищенные методы доступа и мутаторы. Почему: в двух словах, это соглашение простое (один символ), легко читаемое (ваш взгляд не отвлекается на другие ведущие символы) и успешно избегает конфликтов имен с переменными уровня процедуры и свойствами уровня класса. Свойства уровня класса , Я никогда не беспокоюсь об этом, так как я держу все потенциально защищенные переменные-члены в секрете и вместо этого поставляю защищенные методы доступа и мутаторы. Почему: в двух словах, это соглашение простое (один символ), легко читаемое (ваш взгляд не отвлекается на другие ведущие символы) и успешно избегает конфликтов имен с переменными уровня процедуры и свойствами уровня класса. Свойства уровня класса ,


6

Первый комментатор (R Samuel Klatchko) ссылался: каковы правила использования подчеркивания в идентификаторе C ++? который отвечает на вопрос о подчеркивании в C ++. В общем, вы не должны использовать начальное подчеркивание, так как оно зарезервировано для разработчика вашего компилятора. Код, с которым вы сталкиваетесь, _varвероятно, является либо устаревшим кодом, либо кодом, написанным кем-то, кто вырос, используя старую систему именования, которая не зацикливалась на ведущих подчеркиваниях.

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

Я оставлю обсуждение C # другим.


5

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

В C ++ использование соглашения _var является плохой формой, поскольку существуют правила, регулирующие использование подчеркивания перед идентификатором. _var зарезервирован как глобальный идентификатор, а _Var (подчеркивание + заглавная буква) зарезервирован в любое время. Вот почему в C ++ вы увидите людей, которые вместо этого используют соглашение var_.


4

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

Использование _field помогает Intelilsense фильтровать все переменные класса, просто набирая _.

Я обычно следую Руководящим указаниям Брэда Адамса , но он рекомендует не использовать подчеркивание.


2

Именование стандарт Microsoft для C # говорит переменные и параметры должны использовать нижнюю форму верблюд случая IE: paramName . Стандарт также требует поля , чтобы следовать той же форме , но это может привести к неясному коду так много команд требуют подчеркивания префикса для улучшения ясности IE: _fieldName .


2

В C # Руководства по проектированию Microsoft Framework предлагают не использовать символ подчеркивания для открытых членов. Для частных пользователей подчеркивания в порядке. Фактически, Джеффри Рихтер (часто цитируемый в руководящих принципах) использует, например, m_ и s_ для частных статических членов.

Лично я использую только _, чтобы пометить своих частных пользователей. «m_» и «s_» граничат с венгерской нотацией, которая не только не одобряется в .NET, но может быть довольно многословной, и я нахожу, что классы со многими членами трудно выполнить быструю проверку в алфавитном порядке (представьте 10 переменных, все начинающиеся с m_) ,


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

@ JerryNixon-MSFT Идентифицируем ли мы переменный тип данных и тип видимости как одно и то же, когда речь идет о венгерском понятии?
Некро

1

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

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

2) Это помогает в Intellisense (или другой системе дополнения кода), когда я ищу переменную класса. Знание первого символа полезно для фильтрации списка доступных переменных и методов.


1
В более крупном методе возникает сложность - полагаться на intellisense или сканирование при наведении, чтобы увидеть, определен ли var локально или нет. Также предпочитайте "__" для статики по тем же причинам, которые вы упомянули, но в настоящее время это не в пользу.
crokusek

1

Что касается языков C и C ++, то нет особого значения для подчеркивания в имени (начало, середина или конец). Это просто допустимый символ имени переменной. «Соглашения» происходят из практики кодирования в сообществе программистов.

Как уже указывалось различными примерами выше, _ в начале может означать закрытые или защищенные члены класса в C ++.

Позвольте мне привести историю, которая может быть интересной. В UNIX, если у вас есть функция библиотеки ядра C и серверная часть ядра, где вы хотите также представить функцию ядра в пространстве пользователя, _ застревает перед заглушкой функции, которая вызывает функцию ядра напрямую, не делая ничего другого. Наиболее известный и знакомый пример этого - exit () vs _exit () в ядрах типа BSD и SysV: там exit () выполняет работу с пользовательским пространством перед вызовом службы выхода ядра, тогда как _exit просто сопоставляется со службой выхода ядра.

Таким образом, _ использовался для «локальных» вещей, в данном случае локальный был машинно-локальным. Обычно _functions () не были переносимыми. В этом вы не должны ожидать одинакового поведения на разных платформах.

Теперь что касается _ в именах переменных, таких как

int _foo;

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

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


0

Это просто означает, что это поле члена в классе.


0

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


0

Многим людям нравится иметь частные поля с префиксом подчеркивания. Это просто соглашение об именах.

Официальные соглашения об именах в C # предписывают простые строчные имена (без подчеркивания) для частных полей.

Я не знаю стандартных соглашений для C ++, хотя подчеркивания очень широко используются.


0

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

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


0

Существует вполне законная причина использовать его в C #: если код должен быть также расширяемым из VB.NET. (В противном случае я бы не стал.)

Поскольку VB.NET нечувствителен к регистру, fieldв этом коде нет простого способа получить доступ к защищенному члену:

public class CSharpClass
{
    protected int field;
    public int Field { get { return field; } }
}

Например, это получит доступ к получателю свойства, а не к полю:

Public Class VBClass
    Inherits CSharpClass

    Function Test() As Integer
        Return Field
    End Function

End Class

Черт, я даже не могу писать fieldстрочными буквами - VS 2010 просто продолжает исправлять это.

Чтобы сделать его легко доступным для производных классов в VB.NET, необходимо придумать другое соглашение об именах. Префикс подчеркивания является, вероятно, наименее навязчивым и наиболее «исторически приемлемым» из них.


0

Теперь обозначение, использующее this, как в this.foobarbaz, приемлемо для переменных-членов класса C #. Он заменяет старую нотацию "m_" или просто "__". Это делает код более читабельным, потому что нет никаких сомнений в отношении ссылок.


0

Из моего опыта (конечно, ограниченного) подчеркивание будет указывать, что это закрытая переменная-член. Как сказал Голлум, это будет зависеть от команды.


0

Старый вопрос, новый ответ (C #).

Другое использование символов подчеркивания для C # - это DI-ядро ASP NET Core (внедрение зависимостей). Закрытые readonlyпеременные класса, которые были присвоены внедренному интерфейсу во время построения, должны начинаться с подчеркивания. Я предполагаю, что это спор о том, использовать ли подчеркивание для каждого частного члена класса (хотя сама Microsoft следует за ним), но это однозначно.

private readonly ILogger<MyDependency> _logger;

public MyDependency(ILogger<MyDependency> logger)
{
    _logger = logger;
}

-2

Соглашение об именах, подобное этому, полезно, когда вы читаете код, особенно код, который вам не принадлежит. Сильное соглашение об именах помогает указать, где определен конкретный член, какой он член и т. Д. Большинство групп разработчиков применяют простое соглашение об именах и просто ставят префиксы полей для членов с подчеркиванием ( _fieldName). В прошлом я использовал следующее соглашение об именах для C # (основанное на соглашениях Microsoft для кода платформы .NET, которые можно увидеть с помощью Reflector):

Поле экземпляра: m_fieldName
Статическое поле: s_fieldName
Public / Protected / Internal Участник: PascalCasedName ()
Закрытый член: camelCasedName ()

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


3
На самом деле, Microsoft не рекомендует использовать префиксы m_, s_. Не используйте префикс для имен полей. Например, не используйте g_ или s_ для различения статических и нестатических полей. msdn.microsoft.com/en-us/library/ms229012.aspx
Zied

1
Как я уже сказал, взгляните на их исходный код с помощью Reflector. Вы будете удивлены, насколько они уклоняются от своих собственных рекомендаций. ;) Как упоминалось в моем посте, это помогает улучшить читаемость вашего кода, и моей последней команде очень понравилось соглашение об именах, приведенное выше.
Ириста
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.