Я запускал StyleCop над некоторым кодом C #, и он продолжает сообщать, что мои директивы using
должны быть внутри пространство имен.
Есть ли техническая причина для размещения директив using
внутри, а не за пределами пространства имен?
Я запускал StyleCop над некоторым кодом C #, и он продолжает сообщать, что мои директивы using
должны быть внутри пространство имен.
Есть ли техническая причина для размещения директив using
внутри, а не за пределами пространства имен?
На самом деле между ними есть (тонкая) разница. Представьте, что у вас есть следующий код в 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
.
В этой ветке уже есть отличные ответы, но я чувствую, что могу рассказать немного подробнее с этим дополнительным ответом.
Во-первых, помните, что объявление пространства имен с точками, например:
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
, поиск выполняется в следующем порядке:
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 генерировать новый класс в новом файле).
Одно (крошечное) преимущество использования вне заключается в том, что вы можете затем использовать директивы using для глобального атрибута, например [assembly: ComVisible(false)]
вместо [assembly: System.Runtime.InteropServices.ComVisible(false)]
.
Помещение его внутри пространств имен делает объявления локальными для этого пространства имен для файла (в случае, если у вас есть несколько пространств имен в файле), но если у вас есть только одно пространство имен для каждого файла, тогда не имеет большого значения, выходят ли они за пределы или внутри пространства имен.
using ThisNamespace.IsImported.InAllNamespaces.Here;
namespace Namespace1
{
using ThisNamespace.IsImported.InNamespace1.AndNamespace2;
namespace Namespace2
{
using ThisNamespace.IsImported.InJustNamespace2;
}
}
namespace Namespace3
{
using ThisNamespace.IsImported.InJustNamespace3;
}
using
в namespace
блоках могут ссылаться на относительные пространства имен на основе включающего блока namespace
.
- person O. R. Mapper; 09.02.2014
Согласно Hanselman - Using Directive and Assembly Loading ... и другим подобным статьям технически нет разницы.
Я предпочитаю размещать их вне пространств имен.
My style is to put them outside the namespaces.
- почти не ответ вообще.
- person ANeves thinks SE is evil; 22.10.2012
Согласно документации StyleCop:
SA1200: UsingDirectivesMustBePlacedWithinNamespace
Причина Директива 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.Sample содержит определение, конфликтующее с псевдонимом Guid
Код создает псевдоним типа System.Guid с именем Guid, а также создает собственный тип с именем Guid с соответствующим интерфейсом конструктора. Позже код создает экземпляр типа Guid. Чтобы создать этот экземпляр, компилятор должен выбрать между двумя разными определениями Guid. Когда директива using-alias размещается за пределами элемента пространства имен, компилятор выбирает локальное определение Guid, определенное в локальном пространстве имен, и полностью игнорирует директиву using-alias, определенную вне пространства имен. Это, к сожалению, не очевидно при чтении кода.
Однако, когда директива using-alias расположена в пространстве имен, компилятор должен выбирать между двумя разными конфликтующими типами Guid, которые определены в одном пространстве имен. Оба эти типа предоставляют соответствующий конструктор. Компилятор не может принять решение, поэтому он отмечает ошибку компилятора.
Размещение директивы using-alias за пределами пространства имен - плохая практика, потому что это может привести к путанице в таких ситуациях, как эта, когда неочевидно, какая версия типа фактически используется. Это потенциально может привести к ошибке, которую будет сложно диагностировать.
Размещение директив using-alias в элементе пространства имен устраняет это как источник ошибок.
Размещение нескольких элементов пространства имен в одном файле, как правило, является плохой идеей, но если и когда это будет сделано, рекомендуется разместить все директивы using внутри каждого из элементов пространства имен, а не глобально в верхней части файла. Это сильно ограничит пространство имен, а также поможет избежать описанного выше поведения.
Важно отметить, что когда код был написан с использованием директив, размещенных за пределами пространства имен, следует проявлять осторожность при перемещении этих директив в пространстве имен, чтобы гарантировать, что это не изменит семантику кода. Как объяснялось выше, размещение директив using-alias в элементе пространства имен позволяет компилятору выбирать между конфликтующими типами способами, которые не будут происходить, если директивы размещены за пределами пространства имен.
Как исправить нарушения Чтобы исправить нарушение этого правила, переместите все директивы using и директивы using-alias в элемент пространства имен.
using
s вне пространства имен. Внутренний using
s мне кажется таким уродливым. :)
- person nawfal; 06.06.2015
Если вы хотите использовать псевдонимы, возникает проблема с размещением операторов 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
(но не внутри класса / интерфейса / и т. Д.).
- person Jeppe Stig Nielsen; 23.03.2016
using
. Я отредактировал его так, как задумал. Спасибо, что указали. Однако рассуждения все те же.
- person Neo; 23.03.2016
Одна морщина, с которой я столкнулся (которая не рассматривается в других ответах):
Предположим, у вас есть эти пространства имен:
Когда вы используете using Something.Other
вне namespace Parent
, это относится к первому (Something.Other).
Однако, если вы используете его внутри объявления этого пространства имен, он будет ссылаться на второе (Parent.Something.Other)!
Есть простое решение: добавить префикс "global::
": документы
namespace Parent
{
using global::Something.Other;
// etc
}
Как сказал Джеппе Стиг Нильсен , в этой ветке уже есть отличные ответы, но я подумал, что об этой довольно очевидной тонкости тоже стоит упомянуть.
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;
}
}
Еще одна тонкость, которая, как мне кажется, не была охвачена другими ответами, связана с тем, что у вас есть класс и пространство имен с тем же именем.
Когда у вас есть импорт внутри пространства имен, он найдет класс. Если импорт находится за пределами пространства имен, импорт будет проигнорирован, а класс и пространство имен должны быть полностью определены.
//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.
}
}
}
Технические причины обсуждаются в ответах, и я думаю, что в конечном итоге дело доходит до личных предпочтений, поскольку разница не такая большая и есть компромиссы для них обоих. В шаблоне 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"
}
}
}
Вы можете создать этот файл конфигурации на уровне решения и добавить его в свои проекты как «Существующий файл ссылок», чтобы также использовать конфигурацию для всех ваших проектов.
Ссылаясь на внутренние правила Microsoft, имейте в виду, что они написаны кем-то, кто, вероятно, имеет менее десяти лет опыта программирования. Другими словами, они, вероятно, основаны не более чем на личных предпочтениях. Особенно в таких новинках, как C #.
Как правило, внешние директивы using
(например, пространства имен System и Microsoft) следует размещать вне директивы namespace
. Это значения по умолчанию, которые следует применять во всех случаях, если не указано иное. Это должно включать любые внутренние библиотеки вашей организации, которые не являются частью текущего проекта, или директивы using
, которые ссылаются на другие первичные пространства имен в том же проекте. Любые using
директивы, которые ссылаются на другие модули в текущем проекте и пространстве имен, должны быть помещены внутри директивы namespace
. Это выполняет две конкретные функции:
Последняя причина существенна. Это означает, что сложнее ввести неоднозначную справочную проблему, которая может быть вызвана изменением, не более значительным, чем рефакторинг кода. То есть вы перемещаете метод из одного файла в другой, и внезапно обнаруживается ошибка, которой раньше не было. В просторечии «гейзенбаг» - исторически невероятно трудно выследить.
Еще более общее правило, которому следует следовать. Если вы видите что-то присущее языку, которое кажется бесполезным вариантом, предположите, что это НЕ. На самом деле, чем сложнее понять, почему существует такая возможность, тем важнее ее предположить. Изучите конкретные различия между двумя вариантами, а затем хорошенько подумайте о последствиях. Обычно вы найдете удивительно проницательное и умное решение неясной проблемы, которое разработчик языка вставил в него специально для того, чтобы облегчить вам жизнь. Будьте соответственно благодарны и воспользуйтесь этим.
Лучше всего, если те по умолчанию, использующие, например, «ссылки», используемые в исходном решении, должны находиться за пределами пространств имен, а те, которые являются «новой добавленной ссылкой» < / strong> - хорошая практика, вы должны поместить его в пространство имен. Это нужно для того, чтобы различать, какие ссылки добавляются.
using
утверждения; этоusing
директивы. С другой стороны, операторusing
- это языковая структура, которая встречается вместе с другими операторами внутри тела метода и т. Д. Например,using (var e = s.GetEnumerator()) { /* ... */ }
- это оператор, который примерно такой же, какvar e = s.GetEnumerator(); try { /* ... */ } finally { if (e != null) { e.Dispose(); } }
. - person Jeppe Stig Nielsen   schedule 12.04.2017using
внутри объявленийnamespace
в их внутренние правила кодирования - person user1451111   schedule 27.07.2018