Питання Чи слід "використовувати" директиви всередині або за межами простору імен?


Я бігаю StyleCop над деяким кодом C #, і він постійно повідомляє про те, що моє using директиви мають бути всередині простору імен.

Є технічна причина для розміщення using директиви усередині, а не за межами простору імен?


1740
2017-09-24 03:49


походження


Іноді це робить різницю там, де ви використовуєте: stackoverflow.com/questions/292535/linq-to-sql-designer-bug - gius
Просто для довідки, існують додаткові наслідки, крім питання про декілька класів для кожного файлу, тому, якщо ви нещодавно поставили це питання, продовжуйте читати. - Charlie
@ user-12506 - це не дуже добре для середньої та великої групи розробників, де потрібен певний рівень узгодженості коду. І, як зазначалося раніше, якщо ви не розумієте різні макети, ви можете знайти крайові випадки, які не працюють, як ви очікуєте. - benPearce
Термінологія: це не так using  висловлювання; вони є using  директиви. А. using з іншого боку, це структура мови, яка виникає разом з іншими твердженнями в тілі методу і т. д. Як приклад using (var e = s.GetEnumerator()) { /* ... */ } це твердження, що є вільно тим же, що і var e = s.GetEnumerator(); try { /* ... */ } finally { if (e != null) { e.Dispose(); } }. - Jeppe Stig Nielsen
Якщо цього вже не згадував хтось, Майкрософт теж рекомендує ставити using заяви всередині namespace декларації, в їх внутрішні довідники кодування - user1451111


Відповіді:


Існує фактично (тонкий) різниця між цими двома. Уявіть, що у файлі File1.cs є наступний код:

// File1.cs
using System;
namespace Outer.Inner
{
    class Foo
    {
        static void Bar()
        {
            double d = Math.PI;
        }
    }
}

Тепер уявіть, що хтось додає до проекту інший файл (File2.cs), який виглядає так:

// File2.cs
namespace Outer
{
    class Math
    {
    }
}

Компілятор виконує пошук Outer перед тим як дивитися на ці using директиви за межами простору імен, тому він знаходить Outer.Math замість System.Math. На жаль (чи, на щастя?), Outer.Math не має PI член, тому File1 тепер розбитий.

Це змінюється, якщо ви помістіть using Всередині декларації вашого простору імен:

// File1b.cs
namespace Outer.Inner
{
    using System;
    class Foo
    {
        static void Bar()
        {
            double d = Math.PI;
        }
    }
}

Тепер компілятор шукає System перед пошуком Outer, знаходить System.Math, і все добре.

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

Цікаво також відзначити, що станеться, якщо Foo знаходиться в просторі імен Outer, а не Outer.Inner. У цьому випадку додавання Outer.Math в File2 переривається File1 незалежно від того, де using йде Звідси випливає, що компілятор виконує пошук у внутрішньому просторі імен до того, як він виглядає на будь-якому using директива


1843
2017-09-30 02:33



Це є набагато кращою причиною, щоб застосувати твердження, що використовуються, локально, ніж аргумент декількох імен-просторів Марка в одному файлі. Особливо синусою компіляція може і буде скаржитися на зіткнення імен (див. Документацію StyleCop для цього правила (наприклад, як опубліковано Jared)). - David Schmitt
Прийнята відповідь хороша, але мені здається, що є вагомий підхід до застосування статей назовні простір імен. Якщо я знаходжусь у просторі імен Outer.Inner, я б очікував, що він використовуватиме клас математики з Outer.Inner, а не System.Math. - Frank Wallis
Я також з цим погоджуюсь. Прийнята відповідь є правильною, оскільки вона технічно описує різницю. Однак для одного або іншого класу потрібна явна виноска. Я б дуже хотів, щоб у ratehr було рішення "Math" для мого власного локального класу, і System.Math відноситься до зовнішнього класу, навіть якщо System.Math використовувався як "Math", перш ніж Outer.Math існував. Так, це більше роботи, щоб виправити багато попередніх посилань, але це також може бути підказкою, що Outer.Math може мати інше ім'я! - mbmcavoy
Відмінна відповідь, але мені здається, що я хочу лише створити нераспространенные з використанням висловлювань локально, і зберегти структуру з використанням висловлювань глобальної. Будь-хто має додаткове пояснення, чому я повинен повністю змінити мої уподобання? Крім того, звідки вийшло це, шаблони у VS2008 застосовуються поза межами простору імен? - Thymine
Я думаю, що це скоріше погана конвенція імен, а не зміна місця вашого використання. У вашому рішенні не повинно бути класу Math - jDeveloper


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

По-перше, пам'ятайте, що декларація простору імен з періодами, як-от:

namespace MyCorp.TheProduct.SomeModule.Utilities
{
    ...
}

цілком еквівалентно:

namespace MyCorp
{
    namespace TheProduct
    {
        namespace SomeModule
        {
            namespace Utilities
            {
                ...
            }
        }
    }
}

Якщо ти хочеш, ти можеш поставити using директиви на всіх цих рівнях. (Звичайно, ми хочемо мати usings лише в одному місці, але це було б законним за мовою).

Правило вирішення якого типу мається на увазі, може бути вільно викладено таким чином: Спочатку шукайте внутрішню саму "сферу" для матчу, якщо нічого не знайдеться, вийміть один рівень до наступної області та знайдіть там, і так далі., поки не знайдено матч. Якщо на якомусь рівні виявлено більше одного збігу, якщо один із типів є поточним збором, виділіть його та видали попередження компілятора. В іншому випадку відмовитися (помилка складання часу).

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

(1) Використання зовнішньої сторони:

using System;
using System.Collections.Generic;
using System.Linq;
//using MyCorp.TheProduct;  <-- uncommenting this would change nothing
using MyCorp.TheProduct.OtherModule;
using MyCorp.TheProduct.OtherModule.Integration;
using ThirdParty;

namespace MyCorp.TheProduct.SomeModule.Utilities
{
    class C
    {
        Ambiguous a;
    }
}

У вищезгаданому випадку, щоб з'ясувати, який тип Ambiguous є, пошук виконується в такому порядку:

  1. Вкладені типи всередині C (включаючи успадковані вкладені типи)
  2. Типи поточного простору імен MyCorp.TheProduct.SomeModule.Utilities
  3. Типи в просторі імен MyCorp.TheProduct.SomeModule
  4. Типи в MyCorp.TheProduct
  5. Типи в MyCorp
  6. Типи в нуль простір імен (глобальне поле імен)
  7. Типи в System, System.Collections.Generic, System.Linq, MyCorp.TheProduct.OtherModule, MyCorp.TheProduct.OtherModule.Integration, і ThirdParty

Інша конвенція:

(2) Використання всередині:

namespace MyCorp.TheProduct.SomeModule.Utilities
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using MyCorp.TheProduct;                           // MyCorp can be left out; this using is NOT redundant
    using MyCorp.TheProduct.OtherModule;               // MyCorp.TheProduct can be left out
    using MyCorp.TheProduct.OtherModule.Integration;   // MyCorp.TheProduct can be left out
    using ThirdParty;

    class C
    {
        Ambiguous a;
    }
}

Тепер знайдіть тип Ambiguous йде в такому порядку:

  1. Вкладені типи всередині C (включаючи успадковані вкладені типи)
  2. Типи поточного простору імен MyCorp.TheProduct.SomeModule.Utilities
  3. Типи в System, System.Collections.Generic, System.Linq, MyCorp.TheProduct, MyCorp.TheProduct.OtherModule, MyCorp.TheProduct.OtherModule.Integration, і ThirdParty
  4. Типи в просторі імен MyCorp.TheProduct.SomeModule
  5. Типи в MyCorp
  6. Типи в нуль простір імен (глобальне поле імен)

(Зауважте, що MyCorp.TheProduct був частиною "3." і тому не потрібний між "4." і "5.".)

Заключні зауваження

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

Крім того, якщо вставлений імен має те ж саме ім'я, що і тип, це може спричинити проблеми.

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

Шаблони Visual Studio за замовчуванням встановлюють використання назовні простору імен (наприклад, якщо ви створюєте VS у новому файлі нового класу).

Одна (крихітна) перевага у використанні назовні це те, що ви можете потім використовувати директиви для використання для глобального атрибута, наприклад [assembly: ComVisible(false)] замість [assembly: System.Runtime.InteropServices.ComVisible(false)].


346
2018-04-18 21:00



дякую, це набагато краще пояснення, ніж прийнята відповідь. - thor_hayek
Це найкраще пояснення, оскільки він підкреслює той факт, що позиція "використання" тверджень є навмисним рішенням розробника. Ні в якому разі не слід недбало змінювати розташування "використання" тверджень без розуміння наслідків. Тому правило StyleCop просто німий. - ZunTzu


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

using ThisNamespace.IsImported.InAllNamespaces.Here;

namespace Namespace1
{ 
   using ThisNamespace.IsImported.InNamespace1.AndNamespace2;

   namespace Namespace2
   { 
      using ThisNamespace.IsImported.InJustNamespace2;
   }       
}

namespace Namespace3
{ 
   using ThisNamespace.IsImported.InJustNamespace3;
}

178
2017-09-24 03:52



Простір імен забезпечують логічне розділення, а не фізичний (файловий). - Jowen
Це не зовсім так, що немає ніякої різниці; using директиви всередині namespace блоки можуть відноситись до відносних просторів імен на основі вставки namespace блок - O. R. Mapper
так, я знаю. ми встановили, що в цьому питанні прийнята відповідь п'ять років тому. - Mark Cidade


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

Моя перевага полягає в тому, щоб вивести їх за межі простору імен.


56
2017-09-24 03:53



@ Chris M: uh ... посилання, опубліковане в відповіді, вказує на те, що є немає виграш у "проти", фактично показуючи приклад, який фальсифікує вимогу, подану у посилання, яке ви опублікували ... - johnny
Я не повністю прочитав нитку, але купив, коли MVP сказали, що це правильно. Хлопчик спростовує його, пояснює це і показує його код далі ... "IL, що створює компілятор C #, однаковий в обох випадках. Фактично, C # компілятор не створює нічого, що відповідає кожній використаній директиві. Використання директив є суто C # ism, і вони не мають сенсу для самого .NET (неправда для використання тверджень, але це щось зовсім інше.) " groups.google.com/group/wpf-disciples/msg/781738deb0a15c46 - Chris McKee
Будь ласка, додайте резюме посилання. Коли посилання зламане (тому що це воля буває, достатньо часу), раптом відповідь з 32 upvotes тільки варто My style is to put them outside the namespaces. - навряд чи відповідь взагалі. - ANeves
Потреба тут просто неправильна ... є технічна різниця, і ваша власна цитата говорить так ... насправді, це все, про що. Будь ласка, видаліть цю помилкову відповідь ... там набагато краще і точніше. - Jim Balter


Згідно з документацією StyleCop:

SA1200: використанняDirectivesMustBePlacedWithinNamespace

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

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

Наприклад, наступний код призведе до двох порушень цього правила.

using System;
using Guid = System.Guid;

namespace Microsoft.Sample
{
    public class Program
    {
    }
}

Наступний код, однак, не призведе до порушення цього правила:

namespace Microsoft.Sample
{
    using System;
    using Guid = System.Guid;

    public class Program
    {
    }
}

Цей код буде скомпілювати чисто, без будь-яких помилок компілятора. Однак незрозуміло, яку версію типу Guid виділяють. Якщо директива про використання буде переміщена всередині простору імен, як показано нижче, виникатиме помилка компілятора:

namespace Microsoft.Sample
{
    using Guid = System.Guid;
    public class Guid
    {
        public Guid(string s)
        {
        }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            Guid g = new Guid("hello");
        }
    }
}

Код не працює на наступній помилці компілятора, знайденої в рядку, що містить Guid g = new Guid("hello"); 

CS0576: Простір імен 'Microsoft.Sample' містить визначення, яке суперечить псевдоніму 'Guid'

Код створює псевдонім до типу System.Guid, який називається Guid, а також створює власний тип Guid з відповідним інтерфейсом конструктора. Пізніше код створює екземпляр типу Guid. Щоб створити цей екземпляр, компілятор повинен вибрати між двома різними визначеннями Guid. Коли директива з використанням-псевдонімами розміщується за межами елемента простору імен, компілятор вибере локальне визначення Guid, визначене в локальному просторі імен і повністю ігнорує директиву з використанням-псевдонімами, визначену поза межами простору імен. Це, на жаль, не є очевидним при читанні коду.

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

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

Розміщення директорій using-alias в елементі простору імен виключає це як джерело помилок.

  1. Кілька областей імен

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

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

Як виправити порушення Щоб виправити порушення цього правила, перемістіть всі директиви з використанням-псевдонімами в елементі простору імен.


45
2017-09-14 15:17



@Jared - як я зазначив у своєму відповіді, мій переважний варіант вирішення - це мати лише один клас для кожного файлу. Я думаю, що це досить загальна конвенція. - benPearce
Дійсно, це також правило StyleCop! SA1402: Документ C # може містити лише одного класу в кореневому рівні, якщо всі класи не є частковими та мають однаковий тип. Показуючи одне правило, розриваючи інший, просто зливається з неправильним соусом. - Task
Вибачте, що перша відповідь насправді покриває її з точки зору StyleCop. Особисто мені подобається візуальне відчуття usingза межами простору імен. Внутрішній usingЦе виглядає так потворно для мене. :) - nawfal
Нарешті, хороша відповідь на питання. І коментар benPearce не має значення ... це не має нічого спільного з кількістю класів у файлі. - Jim Balter


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

Розглянемо:

namespace MyNamespace
{
    using System;
    using MyAlias = System.DateTime;

    class MyClass
    {
    }
}

проти:

using System;

namespace MyNamespace
{
    using MyAlias = DateTime;

    class MyClass
    {
    }
}

Це може бути особливо виражено, якщо у вас є застарілий псевдонім, наприклад наступне (саме так я знайшов проблему):

using MyAlias = Tuple<Expression<Func<DateTime, object>>, Expression<Func<TimeSpan, object>>>;

З using вирази всередині простору імен це раптом стає:

using MyAlias = System.Tuple<System.Linq.Expressions.Expression<System.Func<System.DateTime, object>>, System.Linq.Expressions.Expression<System.Func<System.TimeSpan, object>>>;

Не гарний


29
2017-10-10 18:47



Твій class потрібна назва (ідентифікатор). Ви не можете мати using директива всередині класу, як ви вкажете. Це має бути на рівні простору імен, наприклад, за межами самого зовнішнього namespace, або просто всередині самого внутрішнього namespace (але не всередині класу / інтерфейсу / тощо). - Jeppe Stig Nielsen
@ JeppeStigNielsen Дякую Я помилявся using директиви помилково. Я відредагував його, як я мав намір це зробити. Дякую, що вказали. Однак аргументи залишаються однаковими. - Neo


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

using Вказані директиви всередині простору імен можуть містити коротший код, оскільки вони не повинні бути повністю кваліфікованими, оскільки вони вказані зовні.

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

Припустимо кодовий файл Foo.cs:

namespace Outer.Inner
{
    class Foo { }
}

І Бар.ч.:

namespace Outer
{
    using Outer.Inner;

    class Bar
    {
        public Foo foo;
    }
}

Це може опустити зовнішнє поле імен в using директива, коротко:

namespace Outer
{
    using Inner;

    class Bar
    {
        public Foo foo;
    }
}

2
2017-09-17 10:32



Це правда, що ви "можете опустити зовнішнє поле назв", але це не означає, що ви повинні. Для мене це ще один аргумент щодо того, чому використання директив (крім псевдонімів, як у відповіді @ Neo) повинно виходити з простору імен, щоб змусити повноцінне імена областей імен. - Keith Robertson


Технічні причини обговорюються у відповідях, і я думаю, що мова йде про особисті уподобання в кінці, оскільки різниця не так великий і існують компроміси для обох. Шаблон за замовчуванням Visual Studio для створення .csфайли використовуються using директиви за межами простору імен, наприклад

Можна настроїти stylecop для перевірки using директиви за межами простору імен через додавання stylecop.json файл у корінь файлу проекту з наступним:

{
  "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
    "orderingRules": {
      "usingDirectivesPlacement": "outsideNamespace"
    }
  }
}

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


0
2018-06-03 12:38





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


-7
2017-10-14 21:30



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