Должны ли использовать "выражения" внутри или вне пространства имен?

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

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

1657
задан benPearce 24 сент. '08 в 6:49
источник поделиться
8 ответов

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

1762
ответ дан Charlie 30 сент. '08 в 5:33
источник поделиться

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

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

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, поиск идет в следующем порядке:

  • Вложенные типы внутри C (включая унаследованные вложенные типы)
  • Типы в текущем пространстве имен MyCorp.TheProduct.SomeModule.Utilities
  • Типы в пространстве имен MyCorp.TheProduct.SomeModule
  • Типы в MyCorp.TheProduct
  • Типы в MyCorp
  • Типы в пустом пространстве имен (глобальное пространство имен)
  • Типы в 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 выполняется в следующем порядке:

  • Вложенные типы внутри C (включая унаследованные вложенные типы)
  • Типы в текущем пространстве имен MyCorp.TheProduct.SomeModule.Utilities
  • Типы в System, System.Collections.Generic, System.Linq, MyCorp.TheProduct, MyCorp.TheProduct.OtherModule, MyCorp.TheProduct.OtherModule.Integration и ThirdParty
  • Типы в пространстве имен MyCorp.TheProduct.SomeModule
  • Типы в MyCorp
  • Типы в пустом пространстве имен (глобальное пространство имен)

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

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

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

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

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

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

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

310
ответ дан Jeppe Stig Nielsen 19 апр. '13 в 0:00
источник поделиться

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

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
ответ дан Mark Cidade 24 сент. '08 в 6:52
источник поделиться

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

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

57
ответ дан Quintin Robinson 24 сент. '08 в 6:53
источник поделиться

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

SA1200: UsingDirectivesMustBePlacedWithinNamespace

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

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

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

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

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

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

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

43
ответ дан JaredCacurak 14 сент. '09 в 18:17
источник поделиться

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

Не очень.

28
ответ дан Neo 10 окт. '12 в 21:47
источник поделиться

Как сказал 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;
    }
}
2
ответ дан Biscuits 17 сент. '16 в 13:32
источник поделиться

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

-5
ответ дан Israel Ocbina 15 окт. '14 в 0:30
источник поделиться

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