создание - что такое пространство имен c#




Должны ли «использовать» директивы внутри или вне пространства имен? (7)

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

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

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

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

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

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

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

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

(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, создаете новый класс в новом файле).

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

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

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


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

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

//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.
        }
    }
}

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


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


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

using ThisNamespace.IsImported.InAllNamespaces.Here;

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

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

namespace Namespace3
{ 
   using ThisNamespace.IsImported.InJustNamespace3;
}

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

SA1200: ИспользованиеDirectivesMustBePlacedWithinNamespace

Причина AC # 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 за пределами пространства имен является плохой практикой, потому что это может привести к путанице в таких ситуациях, когда это не очевидно, какая версия типа фактически используется. Это может привести к ошибке, которая может быть трудно диагностировать.

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

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

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

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

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


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

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

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

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





code-organization