[c#] ‘사용’지시문이 네임 스페이스 내부 또는 외부에 있어야합니까?

C # 코드를 통해 StyleCop 을 실행 했으며 내 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. 여기서 요점은 차이점 있으며 코드 유지 관리에 영향을 미친다는 것입니다.

또한 Foonamespace Outer가 아니라 namespace에 있으면 어떻게되는지 주목하는 것도 흥미 롭습니다 Outer.Inner. 이 경우 Outer.MathFile2에 추가 하면 using이동 위치에 관계없이 File1이 중단 됩니다. 이는 컴파일러가 using지시문을 보기 전에 가장 안쪽의 네임 스페이스를 검색 함을 의미합니다 .


답변

이 스레드에는 이미 훌륭한 답변이 있지만이 추가 답변으로 조금 더 자세하게 가져올 수 있다고 생각합니다.

먼저 마침표가있는 네임 스페이스 선언은 다음과 같습니다.

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가 새 파일에서 새 클래스를 생성하도록하는 경우).

외부 에서 사용하는 것의 가장 작은 장점은 예를 들어 [assembly: ComVisible(false)]대신 전역 속성에 대해 using 지시문을 사용할 수 있다는 것입니다 [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;
}


답변

에 따르면 Hanselman은 – 지침 및 조립로드 … 사용 및 기타 제품은 기술적으로 차이가 없다.

선호하는 것은 네임 스페이스 외부에 배치하는 것입니다.


답변

StyleCop 설명서에 따르면 :

SA1200 : 네임 스페이스에서 지시문 사용

원인 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’와 충돌하는 정의가 포함되어 있습니다.

이 코드는 Guid라는 System.Guid 형식에 대한 별칭을 만들고 일치하는 생성자 인터페이스를 사용하여 Guid라는 자체 형식을 만듭니다. 나중에이 코드는 Guid 유형의 인스턴스를 만듭니다. 이 인스턴스를 만들려면 컴파일러가 Guid의 두 가지 다른 정의 중에서 선택해야합니다. using-alias 지시문이 namespace 요소 외부에 배치되면 컴파일러는 로컬 네임 스페이스 내에 정의 된 Guid의 로컬 정의를 선택하고 네임 스페이스 외부에 정의 된 using-alias 지시문을 완전히 무시합니다. 불행히도 이것은 코드를 읽을 때 분명하지 않습니다.

그러나 using-alias 지시문이 네임 스페이스 내에 있으면 컴파일러는 동일한 네임 스페이스 내에 정의 된 서로 상충되는 두 가지 Guid 유형 중에서 선택해야합니다. 이 두 유형 모두 일치하는 생성자를 제공합니다. 컴파일러가 결정을 내릴 수 없으므로 컴파일러 오류를 표시합니다.

using-alias 지시문을 네임 스페이스 외부에 배치하면 실제로 사용되는 유형의 버전이 확실하지 않은 상황에서 혼동을 일으킬 수 있으므로 나쁜 습관입니다. 이로 인해 잠재적으로 진단하기 어려운 버그가 발생할 수 있습니다.

네임 스페이스 요소 내에 using-alias 지시문을 배치하면이를 버그의 원인으로 사용할 수 없습니다.

  1. 여러 네임 스페이스

단일 파일 내에 여러 네임 스페이스 요소를 배치하는 것은 일반적으로 좋지 않은 방법이지만 이것이 완료되면 전역 적으로 파일의 맨 위가 아닌 각 네임 스페이스 요소 내에 모든 사용 지시문을 배치하는 것이 좋습니다. 이렇게하면 네임 스페이스의 범위가 좁아지고 위에서 설명한 동작을 피하는 데 도움이됩니다.

네임 스페이스 외부에있는 지시문을 사용하여 코드를 작성한 경우 네임 스페이스 내에서 이러한 지시문을 이동할 때 코드의 의미가 변경되지 않도록주의해야합니다. 위에서 설명한 바와 같이, 네임 스페이스 요소 내에 using-alias 지시문을 배치하면 컴파일러가 지시문이 네임 스페이스 외부에 배치 될 때 발생하지 않는 방식으로 충돌하는 유형 중에서 선택할 수 있습니다.

위반을 수정하는 방법이 규칙의 위반을 수정하려면 네임 스페이스 요소 내에서 using using 지시문과 using-alias 지시문을 모두 이동하십시오.


답변

별칭을 사용하려는 경우 네임 스페이스 내에 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>>>;

예쁘지 않은.


답변

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;
    }
}