C # - Чому префікси на полях не перешкоджають?


19

Ще в старі часи ми робили угорські позначення. Це зараз вважається passé , і здебільшого я його більше не використовую, але все одно знаходжу для 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 ++ - ism, де він дозволяє уникнути сутичок імен між полями та методами. У C # це не проблема, оскільки назви методів повинні починатися з верхнього регістру, полів з малого регістру.
амон

4
Якщо ви друкуєте код у 2017 році, ви робите щось не так. В інших двох сценаріях повинно бути досить очевидним, що countне просто з'явилося там, де його не було заявлено в методі. Відверто кажучи, аргумент "поза IDE" дійсно слабкий. Тим більше, що є навіть засоби перегляду коду, які працюють в IDE.
Енді


Відповіді:


19

Коли вони працювали над API для Windows, Microsoft рекомендував використовувати угорські нотації, використовуючи 'm_', а також 'i', 'sz' тощо. У той час це було корисно, оскільки IDE не був надійним достатньо, щоб показати вам цю інформацію.

C #, з іншого боку, має дуже надійний IDE з самого початку.

Тому вони зробили рекомендацію в Посібнику з іменування Microsoft для 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_ є зайвим. Будь-чому, чому ви закріплені на m_? Чому б не l_ (для місцевих)? Чому б не afsdfjdskfjas_? Я збираюся оскаржити це питання, оскільки це все-таки дурний релігійний аргумент. Робити все, що ви хочете. Вказівки, які ви стукаєте, також говорять: "Настанови щодо іменування полів застосовуються до статичних загальнодоступних і захищених полів . Внутрішні та приватні поля _not_ покриті вказівками , а публічні або захищені поля екземплярів заборонені керівництвом щодо проектування членів."
Енді

1
@BettyCrokker Добре, але ви запитуєте про вказівки, зроблені з таким припущенням. Ви також скаржитеся на статичні інструменти, які, ймовірно, також використовують це припущення, тому що "ніхто" (в межах прийнятної статистичної похибки) не виводить свій C # код. Я не знаю, якими інструментами статистичного аналізу ви користуєтесь, але Resharper досить специфічний: приватні поля "_lowerCase". Громадські члени будь-якого типу або "UpperCase", а місцеві жителі - "нижчі". Економія на "м" насправді виглядає досить приємно, так чи інакше.
Кевін Плата

17

У C # немає глобальних змінних, тому залишаються лише локальні змінні та класи класу.

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

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

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


Після того, як ви ввійдете в нього, він також може бути класом або простором імен.
CodesInChaos

3
Я не думаю, що це дійсно відповідає на питання.
Володимир Стокіч

5
@FrankHileman Власне, так і є. Це пояснює, чому немає вагомих причин для того, щоб знехтувати ім’я.
Дедуплікатор

2
"зневажливо"? Якщо це так, це питання, яке стосується лише думки. У кожного може бути різна думка щодо потворності. Є причини віддавати перевагу одній конвенції над іншою, але стандартної конвенції в цьому випадку немає.
Френк Хілеман

1
@ Дедуплікатор краси в очах глядача. Конвенції, які я колись вважав потворними, зараз оцінюю як корисні.
Френк Хілеман

11

Чому розробники уникають попередніх просторів імен за допомогою директиви щодо простору імен?

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

Ні. Ми ледачі друкарки, але це не тому.

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

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


2
Схоже, це не відповідає на питання. Загальних уподобань чи умов для приватних полів у .net немає.
Френк Хілеман

12
@FrankHileman Взагалі ми віддаємо перевагу хорошим іменам. Конвенції не створюють добрих імен. Конвенції створюють такі назви, як McOhPlease Von StopItAlreadyStine.
candied_orange

Конвенції - це просто спростити роботу з кодом різних розробників. Ваша відповідь стосується угорської мови, але для приватних полів немає умов, тому питання ґрунтується на помилковому припущенні.
Френк Хілеман

7

Давайте трохи розширимось.

Існує 3 загальноприйнятих стилю префікса, всі з яких час від часу зустрічаються в коді c #:

  • м_
  • _
  • це.

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

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

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

Перевага this.над m_та _полягає в тому, що ви можете використовувати його в кодовій базі, де _або m_не є прийнятою та універсальною умовою. Це також означає, що нікому не потрібно знати про конвенцію _чи m_конвенцію, яка може розглядатися як спрощення речей. З іншого боку, оскільки компілятор не переймається цим префіксом, this.префікс періодично буде відсутній, і як такий не буде таким надійним, як індикатор.


* Як тільки ви почнете отримувати доступ до класів C #, які використовують _із C ++ / CLI (який насправді не повинен використовувати _), ви помітите, що узгодження загального префікса складніше, ніж повинно бути. Вирішивши не мати префікса, позбавиться від цього шуму. Поєднайте це з відповіддю Дедуплікатора, який я перефразовую як "перевагу префікса майже незначна", і це дає нам: "Існує крихітна проблема при використанні префіксів і крихітна перевернута сторона, таким чином, це правильний вибір просто не використовувати їх ".


З цим питанням є старіше питання про stackoverflow .


1
Приємне порівняння. Однак я вважаю, що невикористання префікса добре працює на практиці.
Phil1970

6

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

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

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

Сказавши, що змінні префікси взагалі відмовляються від кількох причин

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

2
Спочатку я не використовував префікса для полів. Пізніше я перейшов до використання одного символу "_", як я бачив інших, хто використовує його, оскільки він переміщує всі поля до верхньої частини алфавітного списку членів, коли ви використовуєте вікна випадаючого списку IDE. Відсутність стандартної угоди може дратувати при читанні коду, написаного багатьма різними розробниками.
Френк Хілеман

через деякий час ви бачите стільки різних стилів, що ви просто їх ігноруєте. Якби ви спробували нав'язати стиль, ви б постійно переробляли все
Еван

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 # не має цього ключового слова? Ви не можете вказати членів, які використовують це, наприклад, цей.count?

Так, але (1) це більше вводити текст, і (2) легко забути. Якщо поле має ім’я m_count, я не повинен пам'ятати, щоб вводити цей.m_count

Тут, однак, ви помиляєтесь.

Це більше вводити текст, коли ви пишете свій код у Блокноті. З IDE з авто-завершення або, краще, IntelliSense, порівняння між this.countі m_countне настільки очевидна. Зверніть увагу на Shift+, -необхідний для введення підкреслення.

Що стосується "легко забути", це актуально лише для користувачів Блокнота. Не тільки StyleCop і Resharper не дозволять вам забути ставити this.при необхідності, але після декількох годин програмування введення, this.коли потрібно, стає звичкою.


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

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

4

Префікс "member" - це така собі деталь реалізації. Це відволікає вашу увагу від проблемної області до домену технічного рішення, яка упереджує ваш розум, коли думаєте про можливі рефактори. - мене

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

Моя думка полягає у поширенні відповідей @CandiedOrange та @Ewan.

Префікси ускладнюють рефакторинг

У міру того, як ваш код розвивається, змінні та методи можуть змінювати сферу їх застосування. Напр. ви можете створити приватний метод і пізніше з’ясувати, що він повинен бути доступний і для інших (нових) класів. Подібне може статися з параметрами методу та локальними змінними.

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

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 фіксує зміну префікса як зміни в логіці, хоча логіка не змінювалася! Історія СКМ переповнена цими технічними змінами, приховуючи важливе значення та підвищуючи ризик конфліктів із (реальною логікою) іншими змінами.


1

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

Тож у вашому прикладі я мав би:

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