Чому б ви не використовували директиву "використання" в C #?


71

Існуючі стандарти кодування у великому проекті C # містять правило про те, що всі назви типів мають повну кваліфікацію, забороняючи використовувати директиву "використання". Отже, а не знайоме:

using System.Collections.Generic;

.... other stuff ....

List<string> myList = new List<string>();

(Мабуть, не дивно, що varтакож заборонено.)

Я закінчую:

System.Collections.Generic.List<string> myList = new System.Collections.Generic.List<string>();

Це на 134% більше введення тексту, при цьому жодне збільшення не надає корисної інформації. На мою думку, 100% приросту - це шум (безлад), який фактично перешкоджає розумінню.

За 30+ років програмування я бачив такий стандарт, запропонований один чи два рази, але ніколи не реалізований. Обґрунтування, яке це відбувається, уникає мене. Людина, яка нав'язує стандарт, не дурна, і я не думаю, що він зловмисний. Що залишає помилковим як єдину іншу альтернативу, якщо я чогось не пропускаю.

Ви коли-небудь чули про нав'язування такого стандарту? Якщо так, то яка причина була? Чи можете ви придумати аргументи, окрім "це дурно" або "всі інші працюють using", які могли б переконати цю людину зняти цю заборону?

Обґрунтування

Причинами цієї заборони були:

  1. Наводити курсор миші на ім’я досить громіздко, щоб отримати повністю кваліфікований тип. Краще завжди мати повністю видимий тип.
  2. Фрагменти коду електронної пошти не мають повністю кваліфікованого імені, і тому їх важко зрозуміти.
  3. Під час перегляду чи редагування коду поза Visual Studio (наприклад, Блокнот ++) неможливо отримати повноцінне ім’я типу.

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

Про потенційні проблеми конфлікту в просторі імен, які, як я очікував, буде головним питанням, навіть не згадували. Це особливо дивно, оскільки у нас є простір імен MyCompany.MyProject.Core, що є особливо поганою ідеєю. Я давно дізнався, що називати щось Systemабо Coreна C # - це швидкий шлях до божевілля.

Як зазначали інші, конфлікти в просторі імен легко вирішуються шляхом рефакторингу, псевдонімів простору імен або часткової кваліфікації.


Коментарі не для розширеного обговорення; ця розмова перенесена в чат .
maple_shaft

1
Приклад у реальному світі, чому це може бути хорошою ідеєю (але у світі C ++): відповідь на питання Чому "використання простору імен std;" вважається поганою практикою? , поруч із тим, що заборонено як використання директив, так і декларацій.
Пітер Мортенсен

Перш ніж прочитати розділ «Обґрунтування», я якось здогадався про непрактичні причини, оскільки моя компанія має подібні правила.
Gqqnbig

Відповіді:


69

Питання ширше:

Ви коли-небудь чули про нав'язування такого стандарту? Якщо так, то яка причина була?

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


Приклад: такий тип сценарію, ймовірно, краще пояснюється на прикладі.

Скажімо, у нас є два, Lists<T>що належать до двох окремих проектів.

System.Collections.Generic.List<T>
MyCorp.CustomCollections.Optimized.List<T>

Коли ми використовуємо повністю кваліфіковану назву об'єкта, зрозуміло, для чого List<T>використовується. Ця чіткість очевидно виникає ціною багатослівності.

І ви можете сперечатися: "Зачекайте! Ніхто ніколи не використовував би такі списки!" Саме там я зазначу сценарій технічного обслуговування.

Ви написали модуль Fooдля своєї корпорації, який використовує затверджену, оптимізовану корпорацію List<T>.

using MyCorp.CustomCollections.Optimized;

public class Foo {
    List<object> myList = ...;
}

Пізніше новий розробник вирішує продовжити роботу, яку ви робили. Не знаючи стандартів компанії, вони оновлюють usingблок:

using MyCorp.CustomCollections.Optimized;
using System.Collections.Generic;

І ви можете бачити, як поспішають справи.

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

MyCorp.WeightsAndLengths.Measurement();
MyCorp.TimeAndSpace.Measurement();

Реальність:
У даний час, це , ймовірно , відбудеться в більшості проектів? Ні, не дуже. Але працюючи над великим проектом з великою кількістю вкладень, ви робите все можливе, щоб мінімізувати шанси на те, що вибухне на вас.

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

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


Альтернативи:
Хоча ви не питали, варто зазначити, що це не чудове рішення проблеми. Недоцільно створювати класи, які зіткнуться без оголошень у просторі імен.

List<T>, зокрема, слід розглядати як зарезервоване слово, а не використовуватись як назва для ваших занять.

Так само окремі простори імен в рамках проекту повинні прагнути мати унікальні назви класів. Необхідно спробувати згадати, з яким простором імен Foo()ви працюєте - це розумові накладні витрати, яких найкраще уникати. Сказав інший спосіб: мати MyCorp.Bar.Foo()та MyCorp.Baz.Foo()збиратися відключити своїх розробників та краще їх уникати.

Якщо нічого іншого, ви можете використовувати часткові простори імен, щоб вирішити неоднозначність. Наприклад, якщо ви абсолютно не можете перейменувати жоден Foo()клас, ви можете використовувати їх часткові простори імен:

Bar.Foo() 
Baz.Foo()

Конкретні причини вашого поточного проекту:

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

Наводити курсор миші на ім’я досить громіздко, щоб отримати повністю кваліфікований тип. Краще завжди мати повністю видимий тип.

"Громіздкий?" Гм, ні. Прикро, мабуть. Переміщення декількох унцій пластику, щоб зрушити екранний покажчик, не громіздко . Але я відволікаюсь.

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

Це неправдиве виправдання для повнокваліфікованих імен класів, можливо, це дійсне обгрунтування для використання частково кваліфікованих імен класів.

Фрагменти коду електронної пошти не мають повністю кваліфікованого імені, і тому їх важко зрозуміти.

Цей (продовжений?) Рядок міркувань підсилює мою підозру, що класи зараз названі погано. Знову ж таки, наявність поганих імен класу не є хорошим виправданням для того, щоб вимагати від усіх, щоб використовувати повноцінне ім’я класу. Якщо ім'я класу важко зрозуміти, є набагато більше помилок, ніж те, що можуть виправити повноцінні назви класів.

Під час перегляду чи редагування коду поза Visual Studio (наприклад, Блокнот ++) неможливо отримати повноцінне ім’я типу.

З усіх причин ця майже ледь не змусила мене виплюнути свій напій. Але знову я відволікаюсь.

Мені залишається цікаво, чому команда часто редагує чи переглядає код поза Visual Studio? А тепер ми дивимось на виправдання, яке є досить ортогональним щодо того, які простори імен мають надати. Це аргументований аргумент, тоді як простори імен є для надання організаційній структурі коду.

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

Зважаючи на те, що є погано названі класи, і якщо припустити, що ви не можете переробити, правильна відповідь - використовувати ID Visual Studio IDE в повній мірі. Можливо, варто розглянути можливість додавання у плагін, як-от VS PowerTools. Потім, коли я дивлюся, AtrociouslyNamedClass()я можу натиснути на ім'я класу, натиснути клавішу F12 і перейти безпосередньо до визначення класу, щоб краще зрозуміти, що він намагається робити. Так само я можу натиснути Shift-F12, щоб знайти всі плями в коді, які наразі страждають від використання AtrociouslyNamedClass().

Що стосується проблем зовнішнього інструментарію - найкраще зробити це просто зупинити його. Не надсилайте електронні фрагменти вперед і назад, якщо вони не одразу зрозуміли, на що йдеться. Не використовуйте інші інструменти поза Visual Studio, оскільки ці інструменти не мають інтелекту щодо коду, який потрібен вашій команді. Блокнот ++ - дивовижний інструмент, але він не вирізаний для цього завдання.

Тож я погоджуюся з вашою оцінкою щодо трьох конкретних виправдань, які вам були представлені. Це означає, що, на мою думку, вам сказали: «У цьому проекті є основні проблеми, які не можуть / не вирішуються, і саме тому ми їх« виправили ». І це, очевидно, говорить про більш глибокі проблеми в колективі, які можуть послужити для вас червоними прапорами.


1
+1. Я також насправді стикався з деякими неприємними зіткненнями у наших внутрішніх просторах імен. Як при перенесенні застарілого коду до проекту "2.0", так і лише з декількома іменами класів, які семантично дійсні в різних контекстах простору імен. Дійсно хитра справа в тому, що дві речі з однаковою назвою часто матимуть подібні інтерфейси, тому компілятор не буде скаржитися ... ваш час виконання буде!
svidgen

23
Ця проблема вирішується за допомогою псевдонімів простору імен, а не шляхом накладення нерозумних правил, що вимагають коду, затиснутого явним використанням простору імен.
Девід Арно

4
@DavidArno - я не згоден з вами. Однак великі проекти можуть не мати такої можливості. Я просто вказую, чому великий проект може накласти це правило. Я не виступаю за правило. Просто, що іноді потрібно, тому що в команди немає інших варіантів.

4
@ GlenH7, можливо, ви хочете вказати інші доступні рішення у своїй відповіді. Мені б не хотілося, щоб люди натрапляли на це і думали "О. Це прекрасна ідея!" +1, але залишається об'єктивним та прагматичним.
RubberDuck

3
@JimMischel - Це здається, що правило використовується як милиця чогось . Визначити, що це за річ, можливо, неможливо. Так, на високому рівні іноді є обгрунтування того, що не дозволяють, usingsале це не здається, що ваш проект є одним із таких випадків.

16

Я працював в одній компанії, де вони мали багато класів з однаковою назвою, але в різних бібліотеках класів.

Наприклад,

  • Domain.Customer
  • Спадщина. Замовник
  • ServiceX.Customer
  • ViewModel.Customer
  • Database.Customer

Можна сказати, поганий дизайн, але ви знаєте, як органічно розвиваються ці речі, і яка альтернатива - перейменувати всі класи, щоб вони включали простір імен? Основний рефакторинг всього?

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

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

Отже, маючи принаймні частковий простір імен,

var cust = new Domain.Customer

або

public void Purchase(ViewModel.Customer customer)

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

Це був не стандарт кодування, не повний простір імен, і він не застосовувався до всіх класів як стандарт.


13
"яка альтернатива, перейменуйте всі класи, щоб включити простір імен? капітальне рефакторинг всього?" Так, це (правильна) альтернатива.
Девід Арно

2
Тільки в короткостроковій перспективі. Це набагато дешевше, ніж використання явних просторів імен у коді в довгостроковій перспективі.
Девід Арно

3
Радий прийняти цей аргумент, якщо ви платите мені готівкою, а не акціями.
Еван

8
@David: Ні, це НЕ правильна альтернатива. Повна класифікація або всіх ідентифікаторів, або принаймні тих, що насправді стикаються, є правильним способом, і причиною існування просторів імен в першу чергу є забезпечення зіткнень, коли одне і те ж ім’я використовується в різних модулях. Справа в тому, що деякі модулі можуть бути від третіх сторін, кодову базу яких ви не можете змінити. Отже, що ви робите, коли ідентифікатори двох різних сторонніх бібліотек стикаються, і вам потрібно використовувати обидві бібліотеки, але не можете переробити жодну з них? Простори імен існують, оскільки рефакторинг не завжди можливий.
Кайзерлуді

4
@CarlSixsmith, якщо хтось підписується на погляди Мартіна Фаулера на рефакторинг, то якщо зміна вплине на користувача, то ви правильні, це не рефакторинг; це ще одна форма реструктуризації. Це викликає два моменти, хоча: 1. чи всі публічні методи автоматично є частиною API? 2. Сам Фаулер визнає, що він веде програшну битву за це визначення, причому все більша кількість людей використовує термін "рефакторинг" для позначення генералізованої реструктуризації, включаючи публічні зміни.
Девід Арно

7

Немає користі бути занадто багатослівним або занадто коротким: слід знайти золоту середину, щоб бути досить детальною, щоб уникнути тонких помилок, уникаючи при цьому писати занадто багато коду.

У C #, прикладом цього є назви аргументів. Я декілька разів стикався з проблемою, коли я витратив години на пошуки рішення, тоді як більш багатослівний стиль письма міг запобігти помилкам в першу чергу. Проблема складається з помилки в порядку аргументів. Наприклад, чи правильно це вам здається?

if (...) throw new ArgumentNullException("name", "The name should be specified.");
if (...) throw new ArgumentException("name", "The name contains forbidden characters.");

На перший погляд це виглядає ідеально добре, але є помилка. Підписи конструкторів винятків:

public ArgumentException(string message, string paramName)
public ArgumentNullException(string paramName, string message)

Помітили порядок аргументів?

Скориставшись більш багатослівним стилем, де ім'я аргументу вказується кожного разу, коли метод приймає два або більше аргументів одного типу , ми запобігаємо повторенню помилки, і так:

if (...) throw new ArgumentNullException(paramName: "name", message: "The name should be specified.");
if (...) throw new ArgumentException(paramName: "name", message: "The name contains forbidden characters.");

очевидно довше писати, але призводить до меншої витраченості часу на налагодження.

Притиснуті до крайнощів, можна змусити використовувати названі аргументи скрізь , у тому числі в таких методах, як doSomething(int, string)де немає можливості змішати порядок параметрів. Це, ймовірно, завдасть шкоди проекту в довгостроковій перспективі, що призведе до набагато більше коду без користі від попередження помилки, про яку я описав вище.

У вашому випадку, мотивація за «Ні usingправила» в тому , що вона повинна зробити його більш важко використовувати неправильний тип, наприклад, MyCompany.Something.List<T>проти System.Collections.Generic.List<T>.

Особисто я би уникав такого правила, оскільки, IMHO, вартість важко прочитаного коду набагато вище користі від дещо зниженого ризику неправильного використання неправильного типу, але, принаймні, для цього правила є вагома причина.


Такі ситуації можна виявити за допомогою інструментів. ReSharper відзначає paramNameз InvokerParameterNameатрибутом і видає попередження , якщо такий параметр не існує.
Джонбот

2
@Johnbot: вони, звичайно, можуть, але у дуже обмеженій кількості випадків. Що робити, якщо у мене є SomeBusiness.Something.DoStuff(string name, string description)? Жоден інструмент не є розумним, щоб зрозуміти, що таке ім'я та що опис.
Арсеній Муренко

@ArseniMourzenko в цьому випадку навіть людині потрібно зайняти час, з'ясовуючи, правильна виклик чи ні.
Gqqnbig

6

Простори імен надають коду ієрархічну структуру, що дозволяє скорочувати імена.

   MyCompany.Headquarters.Team.Leader
   MyCompany.Warehouse.Team.Leader

Якщо ви дозволите ключове слово "використовувати", існує ланцюжок подій ...

  • Усі реально використовують один глобальний простір імен
    (тому що вони включають усі простори імен, які вони могли б хотіти)
  • Люди починають отримувати сутички з іменами, або
  • переживайте за отримання сутичок з іменами.

Щоб уникнути ризику, кожен починає використовувати дійсно довгі імена:

   HeadquartersTeamLeader
   WarehouseTeamLeader

Ситуація посилюється ...

  • Хтось ще, можливо, вже вибрав ім’я, тому ви використовуєте довше.
  • Назви стають все довше і довше.
  • Довжина може перевищувати 60 символів, максимум - це стає смішно.

Я бачив, як це відбувається, тому можу співчувати компаніям, які забороняють "використовувати".


11
Заборона using- це ядерний варіант. Псевдоніми простору імен та часткова кваліфікація є кращими.
Джим Мішель

1

Проблема usingполягає в тому, що він чарівно додає до простору імен без явного оголошення. Це набагато більше проблеми для програміста з технічного обслуговування, який стикається з чимось на кшталт List<>і не знаючи домену, не знає, який usingпункт його ввів.

Подібна проблема виникає в Java, якщо писати, import Java.util.*;а не import Java.util.List;наприклад; остання декларація документує, яку назву було введено, щоб, коли вона List<>з’являється пізніше у джерелі, її походження стало відомо з importдекларації.


6
Оскільки це правда, що хтось, незнайомий з доменом, матиме певні труднощі, все, що він повинен зробити, - навести курсор миші на ім’я типу, і він отримає повністю кваліфіковане ім’я. Або він може клацнути правою кнопкою миші та перейти безпосередньо до визначення типу. Для типів .NET Framework він, мабуть, уже знає, де такі речі. Для типів, що стосуються домену, йому знадобиться, можливо, тиждень, щоб стати комфортним. Тоді цілком кваліфіковані імена - це лише шум, що перешкоджає розумінню.
Джим Мішель

Джим .. Я просто спробував навести мишу на визначення, і це не вийшло. Чи розглядали ви можливість того, що деякі програмісти у світі не використовують Microsoft IDE? У будь-якому випадку, ваш лічильник не визнає мою думку про те, що джерело з використанням більше не є
самодокументуванням

2
Так, є люди, які працюють в C #, які перешкоджають собі тим, що не використовують найкращі доступні інструменти. І хоча аргумент того, що повна кваліфікація є "самодокументуванням", є певним достоїнством, такий код має величезну вартість для читання. Весь цей сторонній код створює шум, який затемнює частини коду, які насправді мають значення.
Джим Мішель
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.