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

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

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

вопрос задан 24.09.2008
benPearce
18962 репутация

10 ответов


  • 1861 рейтинг

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

    ответ дан Charlie, с репутацией 32917, 30.09.2008
  • 352 рейтинг

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

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

    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. Типы в пространстве имен null (глобальное пространство имен)
    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. Типы в пространстве имен null (глобальное пространство имен)

    (Обратите внимание, что MyCorp.TheProduct был частью "3. «и поэтому не было необходимости между» 4. "и" 5. ». )

    Заключительные замечания

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

    Кроме того, если вложенное пространство имен имеет то же имя, что и тип, это может вызвать проблемы.

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

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

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

    ответ дан Jeppe Stig Nielsen, с репутацией 41844, 18.04.2013
  • 182 рейтинг

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

    using ThisNamespace.IsImported.InAllNamespaces.Here;
    
    namespace Namespace1
    { 
       using ThisNamespace.IsImported.InNamespace1.AndNamespace2;
    
       namespace Namespace2
       { 
          using ThisNamespace.IsImported.InJustNamespace2;
       }       
    }
    
    namespace Namespace3
    { 
       using ThisNamespace.IsImported.InJustNamespace3;
    }
    
    ответ дан Mark Cidade, с репутацией 82855, 24.09.2008
  • 58 рейтинг

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

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

    ответ дан Quintin Robinson, с репутацией 66466, 24.09.2008
  • 45 рейтинг

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

    SA1200: использование директив MustBePlacedWithinNamespace

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

    Описание правила Нарушение этого правила происходит, когда директива 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. Образец »содержит определение, конфликтующее с псевдонимом« Guid »

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

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

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

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

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

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

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

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

    ответ дан JaredCacurak, с репутацией 766, 14.09.2009
  • 31 рейтинг

    Существует проблема с размещением операторов 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>>;
    

    С using утверждениями внутри пространства имен это внезапно становится:

    using MyAlias = System.Tuple>, System.Linq.Expressions.Expression>>;
    

    Не красиво.

    ответ дан Neo, с репутацией 1376, 10.10.2012
  • 3 рейтинг

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

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

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

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

    namespace Outer.Inner
    {
        class Foo { }
    }
    

    и Бар. CS :

    namespace Outer
    {
        using Outer.Inner;
    
        class Bar
        {
            public Foo foo;
        }
    }
    

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

    namespace Outer
    {
        using Inner;
    
        class Bar
        {
            public Foo foo;
        }
    }
    
    ответ дан Biscuits, с репутацией 1237, 17.09.2016
  • 1 рейтинг

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

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

    //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.
            }
        }
    }
    
    ответ дан Ben Gardner, с репутацией 16, 24.08.2018
  • 0 рейтинг

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

    Можно настроить stylecop для проверки директив using вне пространств имен, добавив файл stylecop.json в корень файла проекта с помощью следующего:

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

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

    ответ дан sotn, с репутацией 1405, 3.06.2018
  • -7 рейтинг

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

    ответ дан Israel Ocbina, с репутацией 156, 14.10.2014