Должны ли директивы using находиться внутри или вне пространства имен?

Я запускал StyleCop над некоторым кодом C#, и он продолжает сообщать, что мои директивы using должны находиться внутри пространства имен.

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

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

Charlie 22.01.2010 20:23

Иногда имеет значение, где вы указываете использование: stackoverflow.com/questions/292535/linq-to-sql-designer-bug

gius 12.12.2008 21:15

@ user-12506 - это не очень хорошо работает в средней и большой команде разработчиков, где требуется некоторый уровень согласованности кода. И, как отмечалось ранее, если вы не понимаете различных макетов, вы можете обнаружить крайние случаи, которые работают не так, как вы ожидаете.

benPearce 04.02.2014 02:28

Терминология: Это не usingзаявления; это usingдирективы. С другой стороны, оператор using - это языковая структура, которая встречается вместе с другими операторами внутри тела метода и т. д. Например, using (var e = s.GetEnumerator()) { /* ... */ } - это оператор, который примерно такой же, как var e = s.GetEnumerator(); try { /* ... */ } finally { if (e != null) { e.Dispose(); } }.

Jeppe Stig Nielsen 12.04.2017 00:30

Если об этом уже никто не упоминал, на самом деле Microsoft тоже рекомендует помещать операторы using внутри объявлений namespace, в их рекомендации по внутреннему кодированию.

user1451111 27.07.2018 05:15
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2 170
5
223 612
12
Перейти к ответу Данный вопрос помечен как решенный

Ответы 12

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

using ThisNamespace.IsImported.InAllNamespaces.Here;

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

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

namespace Namespace3
{ 
   using ThisNamespace.IsImported.InJustNamespace3;
}

пространства имен обеспечивают логическое разделение, а не физическое (файловое).

Jowen 26.09.2013 15:08

Не совсем верно, что нет разницы; Директивы using в блоках namespace могут ссылаться на относительные пространства имен на основе включающего блока namespace.

O. R. Mapper 09.02.2014 14:07

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

Mark Cidade 11.02.2014 01:58

Согласно Hanselman - Использование директивы и сборки Загрузка ... и другим подобным статьям, технически разницы нет.

Я предпочитаю размещать их вне пространств имен.

@Chris M: эээ ... ссылка, опубликованная в ответе, указывает на преимущество нет по сравнению с in vs. out, фактически показывая пример, который фальсифицирует утверждение, сделанное в ссылке, которую вы разместили ...

johnny 19.08.2011 22:07

Да, я не полностью прочитал ветку, но согласился, когда MVP сказали, что это правильно. Парень опровергает это, объясняет это и показывает свой код ниже ... "IL, который генерирует компилятор C#, одинаков в любом случае. Фактически компилятор C# не генерирует ничего, соответствующего каждой директиве using. Директивы using - это чисто C# ism, и они не имеют никакого значения для самой .NET. (Не верно для операторов using, но это нечто совсем другое.) "groups.google.com/group/wpf-disciples/msg/781738deb0a15c46

Chris McKee 25.08.2011 01:52

Пожалуйста, включите краткое содержание ссылки. Когда ссылка не работает (потому что это происходит воля при наличии достаточного времени), внезапно ответ с 32 положительными голосами стоит только My style is to put them outside the namespaces. - едва ли вообще ответ.

ANeves thinks SE is evil 22.10.2012 11:45

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

Jim Balter 17.04.2016 06:44
Ответ принят как подходящий

На самом деле между ними есть (тонкая) разница. Представьте, что у вас есть следующий код в 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.

Согласно документации StyleCop:

SA1200: UsingDirectivesMustBePlacedWithinNamespace

Причина Директива C# using помещается вне элемента пространства имен.

Описание правила Нарушение этого правила происходит, когда директива using или директива using-alias размещается за пределами элемента пространства имен, если файл не содержит никаких элементов пространства имен.

Например, следующий код приведет к двум нарушениям этого правила.

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 выделяется. Если директива using переместится внутрь пространства имен, как показано ниже, произойдет ошибка компилятора:

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. Когда директива using-alias размещается за пределами элемента пространства имен, компилятор выбирает локальное определение Guid, определенное в локальном пространстве имен, и полностью игнорирует директиву using-alias, определенную вне пространства имен. Это, к сожалению, не очевидно при чтении кода.

Однако, когда директива using-alias расположена в пространстве имен, компилятор должен выбирать между двумя разными конфликтующими типами Guid, которые определены в одном пространстве имен. Оба эти типа предоставляют соответствующий конструктор. Компилятор не может принять решение, поэтому он отмечает ошибку компилятора.

Размещение директивы using-alias вне пространства имен - плохая практика, поскольку это может привести к путанице в таких ситуациях, как эта, где не очевидно, какая версия типа фактически используется. Это потенциально может привести к ошибке, которую будет сложно диагностировать.

Размещение директив using-alias в элементе пространства имен устраняет это как источник ошибок.

  1. Несколько пространств имен

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

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

Как исправить нарушения Чтобы исправить нарушение этого правила, переместите все директивы using и директивы using-alias в элемент пространства имен.

@Jared - как я отметил в своем ответе, я предпочитаю обходной путь / решение - всегда иметь только один класс для каждого файла. Я считаю, что это довольно распространенная конвенция.

benPearce 15.09.2009 01:01

В самом деле, это тоже правило StyleCop! SA1402: документ C# может содержать только один класс на корневом уровне, если все классы не являются частичными и относятся к одному типу. Продемонстрировать одно правило, нарушив другое, просто капает с неправильным соусом.

Task 05.03.2010 21:09

Проголосовали за то, чтобы быть первым ответом, который действительно освещает это с точки зрения StyleCop. Лично мне нравится визуальное восприятие using за пределами пространства имен. Внутренние using кажутся мне такими уродливыми. :)

nawfal 06.06.2015 13:45

Наконец-то хороший ответ на вопрос. И комментарий benPearce не имеет значения ... это не имеет ничего общего с количеством классов в файле.

Jim Balter 17.04.2016 06:51

Если вы хотите использовать псевдонимы, возникает проблема с размещением операторов using внутри пространства имен. Псевдоним не использует более ранние утверждения 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>>>;

Не очень.

Вашему class необходимо имя (идентификатор). У вас не может быть директивы using внутри класса, как вы указываете. Он должен находиться на уровне пространства имен, например, вне самого внешнего namespace или только внутри самого внутреннего namespace (но не внутри класса / интерфейса / и т. д.).

Jeppe Stig Nielsen 23.03.2016 14:51

@JeppeStigNielsen Спасибо. Я ошибочно разместил директивы using. Я отредактировал его так, как задумал. Спасибо, что указали. Однако рассуждения все те же.

Neo 23.03.2016 23:54

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

Во-первых, помните, что объявление пространства имен с точками, например:

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

полностью эквивалентен:

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

Если вы хотите, вы можете разместить директивы using на всех этих уровнях. (Конечно, мы хотим иметь using только в одном месте, но это было бы законно в зависимости от языка.)

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

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

(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 генерировать новый класс в новом файле).

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

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

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

Abel 03.11.2014 17:01

Как и Jeppe Stig Nielsen сказал, в этой ветке уже есть отличные ответы, но я подумал, что об этой довольно очевидной тонкости тоже стоит упомянуть.

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

Следующий пример работает, потому что типы Foo и Bar находятся в одном и том же глобальном пространстве имен Outer.

Предположим, файл кода Foo.cs:

namespace Outer.Inner
{
    class Foo { }
}

И Bar.cs:

namespace Outer
{
    using Outer.Inner;

    class Bar
    {
        public Foo foo;
    }
}

Это может опустить внешнее пространство имен в директиве using, для краткости:

namespace Outer
{
    using Inner;

    class Bar
    {
        public Foo foo;
    }
}

Это правда, что вы «можете опустить внешнее пространство имен», но это не значит, что вы должны это делать. Для меня это еще один аргумент относительно того, почему директивы using (кроме псевдонимов, как в ответе @ Neo) должны выходить за пределы пространства имен, чтобы принудительно использовать полностью определенные имена пространств имен.

Keith Robertson 03.11.2016 18:39

Технические причины обсуждаются в ответах, и я думаю, что в конечном итоге дело доходит до личных предпочтений, поскольку разница не в том, что большой, и есть компромиссы для них обоих. В шаблоне 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"
    }
  }
}

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

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

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

//file1.cs
namespace Foo
{
    class Foo
    {
    }
}

//file2.cs
namespace ConsoleApp3
{
    using Foo;
    class Program
    {
        static void Main(string[] args)
        {
            //This will allow you to use the class
            Foo test = new Foo();
        }
    }
}

//file2.cs
using Foo; //Unused and redundant    
namespace Bar
{
    class Bar
    {
        Bar()
        {
            Foo.Foo test = new Foo.Foo();
            Foo test = new Foo(); //will give you an error that a namespace is being used like a class.
        }
    }
}

Одна морщина, с которой я столкнулся (которая не рассматривается в других ответах):

Предположим, у вас есть эти пространства имен:

  • Что-то другое
  • Родитель.Что-то.Другое

Когда вы используете using Something.Otherвне из namespace Parent, это относится к первому (Something.Other).

Однако, если вы используете его внутри этого объявления пространства имен, он ссылается на второй (Parent.Something.Other)!

Есть простое решение: добавить префикс «global::»: документы

namespace Parent
{
   using global::Something.Other;
   // etc
}

Хорошая точка зрения! Я столкнулся с проблемой, связанной с работой с RazorGenerator. К сожалению, похоже, что он не понимает 'global ::' и преобразует его в 'using global;' :( Жаль, что я не могу выбрать, нужно ли сгенерировать 'using' внутри или за пределами 'namespace' ...

Alexander 05.01.2021 03:50

Ссылаясь на внутренние правила Microsoft, имейте в виду, что они написаны кем-то, кто, вероятно, имеет менее десяти лет опыта программирования. Другими словами, они, вероятно, основаны не более чем на личных предпочтениях. Особенно в таких новинках, как C#.

Как правило, внешние директивы using (например, пространства имен System и Microsoft) следует размещать как вне директивы namespace. Это значения по умолчанию, которые следует применять во всех случаях если не указано иное. Это должно включать любые внутренние библиотеки вашей организации, которые не являются частью текущего проекта, или директивы using, которые ссылаются на другие первичные пространства имен в том же проекте. Любые директивы using, которые ссылаются на другие модули в текущем проекте и пространстве имен, должны быть помещены внутри в директиву namespace. Это выполняет две конкретные функции:

  • Он обеспечивает визуальное различие между локальными модулями и «другими» модулями, то есть всем остальным.
  • Он охватывает локальные директивы, которые будут применяться предпочтительно над глобальными директивами.

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

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

@jwdonahue: Когда меня пригласили присоединиться к команде разработчиков функций C#, я почти десять лет был профессиональным разработчиком компиляторов и языковым дизайнером, и я был безусловно самым молодым человеком в комнате. Команда разработчиков C# наверняка воспользуется прекрасными идеями, исходящими от стажеров; представление о недостатке опыта в проектировании, оценке, определении и реализации языковых функций в группе разработчиков C# не соответствует действительности.

Eric Lippert 05.03.2021 11:51

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

Eric Lippert 05.03.2021 11:54

@EricLippert, оглядываясь назад, я не должен был озвучивать эту глупую мысль. Извинения.

jwdonahue 06.03.2021 05:26

Другие вопросы по теме