Vraag Moeten 'gebruikende' richtlijnen zich binnen of buiten de naamruimte bevinden?


ik heb gerend StyleCop via een of andere C # -code, en deze blijft die van mij melden using richtlijnen moeten zich in de naamruimte bevinden.

Is er een technische reden om de using richtlijnen in plaats van buiten de naamruimte?


1737
2017-09-24 03:49


oorsprong


antwoorden:


Er is eigenlijk een (subtiel) verschil tussen de twee. Stel je voor dat je de volgende code hebt in File1.cs:

// File1.cs
using System;
namespace Outer.Inner
{
    class Foo
    {
        static void Bar()
        {
            double d = Math.PI;
        }
    }
}

Stel je nu voor dat iemand een ander bestand (File2.cs) aan het project toevoegt dat er zo uitziet:

// File2.cs
namespace Outer
{
    class Math
    {
    }
}

De compiler zoekt Outer alvorens naar die te kijken using richtlijnen buiten de naamruimte, dus het vindt Outer.Math in plaats van System.Math. Helaas (of misschien gelukkig?), Outer.Math heeft geen PI lid, dus File1 is nu verbroken.

Dit verandert als je de using in je naamruimtedeclaratie, als volgt:

// File1b.cs
namespace Outer.Inner
{
    using System;
    class Foo
    {
        static void Bar()
        {
            double d = Math.PI;
        }
    }
}

Nu zoekt de compiler System voor het zoeken Outer, vondsten System.Mathen alles is goed.

Sommigen beweren dat Math is misschien een slechte naam voor een door de gebruiker gedefinieerde klasse, omdat er al een in staat System; het punt is hier alleen dat daar is een verschil, en dit beïnvloedt de onderhoudbaarheid van uw code.

Het is ook interessant om op te merken wat er gebeurt als Foo bevindt zich in de naamruimte Outer, liever dan Outer.Inner. In dat geval, toevoegen Outer.Math in File2 breekt File1 ongeacht waar de using gaat. Dit impliceert dat de compiler de binnenste omhullende naamruimte doorzoekt voordat deze naar een ervan kijkt using richtlijn.


1839
2017-09-30 02:33



Deze thread heeft al enkele geweldige antwoorden, maar ik denk dat ik wat meer details kan toevoegen met dit aanvullende antwoord.

Onthoud eerst dat een naamruimtedeclaratie met punten, zoals:

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

is volledig gelijk aan:

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

Als je wilde, zou je kunnen zetten using richtlijnen op al deze niveaus. (Natuurlijk willen we dat hebben usings op slechts één plaats, maar het zou legaal zijn volgens de taal.)

De regel voor het oplossen van welk type wordt geïmpliceerd, kan zo losjes worden vermeld: Zoek eerst het meest binnenste 'bereik' voor een overeenkomst, als er niets wordt gevonden, gaat u op een niveau naar het volgende bereik en zoekt u daar, enzovoort, totdat een overeenkomst is gevonden. Als op een bepaald niveau meer dan één overeenkomst wordt gevonden, kiest u een van de typen uit de huidige assembly en kiest u een compilerwaarschuwing. Anders geef je op (compile-time error).

Laten we nu expliciet zijn over wat dit betekent in een concreet voorbeeld met de twee belangrijkste conventies.

(1) Met gebruik buiten:

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

In het bovenstaande geval om uit te zoeken welk type Ambiguous is, gaat de zoekopdracht in deze volgorde:

  1. Geneste typen binnen C (inclusief overgenomen geneste typen)
  2. Typen in de huidige naamruimte MyCorp.TheProduct.SomeModule.Utilities
  3. Typen in naamruimte MyCorp.TheProduct.SomeModule
  4. Typen in MyCorp.TheProduct
  5. Typen in MyCorp
  6. Typen in de nul naamruimte (de globale naamruimte)
  7. Typen in System, System.Collections.Generic, System.Linq, MyCorp.TheProduct.OtherModule, MyCorp.TheProduct.OtherModule.Integration, en ThirdParty

De andere conventie:

(2) Met usings inside:

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

Zoek nu naar het type Ambiguous gaat in deze volgorde:

  1. Geneste typen binnen C (inclusief overgenomen geneste typen)
  2. Typen in de huidige naamruimte MyCorp.TheProduct.SomeModule.Utilities
  3. Typen in System, System.Collections.Generic, System.Linq, MyCorp.TheProduct, MyCorp.TheProduct.OtherModule, MyCorp.TheProduct.OtherModule.Integration, en ThirdParty
  4. Typen in naamruimte MyCorp.TheProduct.SomeModule
  5. Typen in MyCorp
  6. Typen in de nul naamruimte (de globale naamruimte)

(Let daar op MyCorp.TheProduct was een onderdeel van "3." en was daarom niet nodig tussen "4." en "5.".)

Slotopmerkingen

Het maakt niet uit of u de usings binnen of buiten de naamruimtedeclaratie plaatst, er is altijd de mogelijkheid dat iemand later een nieuw type met dezelfde naam toevoegt aan een van de namespaces die een hogere prioriteit hebben.

Als een geneste naamruimte dezelfde naam heeft als een type, kan dit ook problemen veroorzaken.

Het is altijd gevaarlijk om de usings van de ene locatie naar de andere te verplaatsen, omdat de zoekhiërarchie verandert en een ander type kan worden gevonden. Kies daarom een ​​conventie en blijf erbij, zodat u nooit uw voorkeuren hoeft te verplaatsen.

De sjablonen van Visual Studio plaatsen standaard de usings buiten van de naamruimte (bijvoorbeeld als u VS een nieuwe klasse in een nieuw bestand laat genereren).

Een (klein) voordeel van het hebben van usings buiten is dat u vervolgens de gebruiksrichtlijnen kunt gebruiken voor een globaal attribuut, bijvoorbeeld [assembly: ComVisible(false)] in plaats van [assembly: System.Runtime.InteropServices.ComVisible(false)].


345
2018-04-18 21:00



Als u deze in de naamruimten plaatst, worden de declaraties lokaal in die naamruimte voor het bestand (als u meerdere naamruimten in het bestand hebt) maar als u slechts één naamruimte per bestand heeft, maakt het niet veel uit of ze naar buiten gaan of in de naamruimte.

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
2017-09-24 03:52



Volgens Hanselman - Richtlijn en assembly gebruiken Bezig met laden ... en andere dergelijke artikelen is er technisch gezien geen verschil.

Mijn voorkeur gaat ernaar om ze buiten de naamruimten te plaatsen.


56
2017-09-24 03:53



Volgens de tot StyleCop-documentatie:

SA1200: UsingDirectivesMustBePlacedWithinNamespace

Oorzaak Een C # -richtlijn wordt buiten een naamruimte-element geplaatst.

Regel beschrijving Een overtreding van deze regel treedt op wanneer een gebruikrichtlijn of een aliasgebruiksrichtlijn buiten een naamruimte-element wordt geplaatst, tenzij het bestand geen naamruimte-elementen bevat.

De volgende code zou bijvoorbeeld resulteren in twee schendingen van deze regel.

using System;
using Guid = System.Guid;

namespace Microsoft.Sample
{
    public class Program
    {
    }
}

De volgende code zou echter niet resulteren in een overtreding van deze regel:

namespace Microsoft.Sample
{
    using System;
    using Guid = System.Guid;

    public class Program
    {
    }
}

Deze code compileert correct, zonder compilerfouten. Het is echter onduidelijk welke versie van het Guid-type wordt toegewezen. Als de gebruiksrichtlijn binnen de naamruimte wordt verplaatst, zoals hieronder wordt weergegeven, treedt een compileerfout op:

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

De code mislukt bij de volgende compileerfout, gevonden op de regel met Guid g = new Guid("hello"); 

CS0576: naamruimte 'Microsoft.Sample' bevat een definitie die conflicteert met de alias 'Guid'

De code maakt een alias voor het System.Guid-type Guid, en maakt ook een eigen type met de naam Guid met een overeenkomende constructorinterface. Later maakt de code een instantie van het type Guid. Om dit exemplaar te maken, moet de compiler kiezen tussen de twee verschillende definities van Guid. Wanneer de instructie use-alias buiten het naamruimte-element wordt geplaatst, kiest de compiler de lokale definitie van Guid die is gedefinieerd in de lokale naamruimte en negeert volledig de using-alias-instructie die buiten de naamruimte is gedefinieerd. Dit is helaas niet duidelijk bij het lezen van de code.

Wanneer de instructie use-alias binnen de naamruimte wordt geplaatst, moet de compiler echter kiezen tussen twee verschillende, conflicterende Guid-typen die beide in dezelfde naamruimte zijn gedefinieerd. Beide typen bieden een bijpassende constructor. De compiler kan geen beslissing nemen en markeert daarom de compileerfout.

Het plaatsen van de alias using-alias buiten de naamruimte is een slechte gewoonte, omdat dit kan leiden tot verwarring in situaties zoals deze, waarbij het niet voor de hand ligt welke versie van het type daadwerkelijk wordt gebruikt. Dit kan mogelijk leiden tot een bug die mogelijk moeilijk te diagnosticeren is.

Door het gebruik van alias-instructies binnen het naamruimte-element wordt dit als een bron van fouten geëlimineerd.

  1. Meerdere naamruimten

Het plaatsen van meerdere naamruimtecomponenten in een enkel bestand is over het algemeen een slecht idee, maar als dit is gebeurd, is het een goed idee om alle richtlijnen in elk van de naamruimte-elementen te plaatsen, in plaats van globaal boven aan het bestand. Dit zal de namespaces strak afbakenen en zal ook helpen om het soort gedrag dat hierboven is beschreven te vermijden.

Het is belangrijk om te weten dat wanneer code is geschreven met behulp van richtlijnen die buiten de naamruimte zijn geplaatst, er voorzichtig mee moet worden omgegaan als deze richtlijnen binnen de naamruimte worden verplaatst, om ervoor te zorgen dat dit de semantiek van de code niet verandert. Zoals hierboven uitgelegd, stelt het plaatsen van alias-instructies binnen het naamruimte-element de compiler in staat te kiezen tussen conflicterende typen op manieren die niet zullen gebeuren wanneer de instructies buiten de naamruimte worden geplaatst.

Schendingen oplossen Om een ​​overtreding van deze regel te verhelpen, verplaatst u alle met behulp van richtlijnen en het gebruik van alias-instructies binnen het naamruimteelement.


45
2017-09-14 15:17



Er is een probleem met het plaatsen van instructies in de naamruimte wanneer u aliassen wilt gebruiken. Het alias profiteert niet van het eerdere using verklaringen en moet volledig gekwalificeerd zijn.

Overwegen:

namespace MyNamespace
{
    using System;
    using MyAlias = System.DateTime;

    class MyClass
    {
    }
}

versus:

using System;

namespace MyNamespace
{
    using MyAlias = DateTime;

    class MyClass
    {
    }
}

Dit kan in het bijzonder uitgesproken zijn als je een langdradig alias hebt zoals het volgende (en zo heb ik het probleem gevonden):

using MyAlias = Tuple<Expression<Func<DateTime, object>>, Expression<Func<TimeSpan, object>>>;

Met using binnen de naamruimte, wordt het plotseling:

using MyAlias = System.Tuple<System.Linq.Expressions.Expression<System.Func<System.DateTime, object>>, System.Linq.Expressions.Expression<System.Func<System.TimeSpan, object>>>;

Niet mooi.


29
2017-10-10 18:47



Zoals Jeppe Stig Nielsen zei, deze thread heeft al geweldige antwoorden, maar ik dacht dat deze nogal voor de hand liggende subtiliteit het vermelden waard was.

using Richtlijnen die binnen namespaces worden opgegeven, kunnen zorgen voor kortere code, omdat ze niet volledig hoeven te worden gekwalificeerd als wanneer ze aan de buitenkant zijn opgegeven.

Het volgende voorbeeld werkt omdat de typen Foo en Bar bevinden zich allebei in dezelfde globale naamruimte, Outer.

Stel het codebestand in Foo.cs:

namespace Outer.Inner
{
    class Foo { }
}

En Bar.cs:

namespace Outer
{
    using Outer.Inner;

    class Bar
    {
        public Foo foo;
    }
}

Dat kan de outer namespace in de using richtlijn, kortom:

namespace Outer
{
    using Inner;

    class Bar
    {
        public Foo foo;
    }
}

2
2017-09-17 10:32



De technische redenen worden in de antwoorden besproken en ik denk dat het uiteindelijk gaat om de persoonlijke voorkeuren, omdat het verschil niet zo is groot en er zijn afwegingen voor beiden. Visual Studio's standaardsjabloon voor het maken .csbestanden gebruiken using richtlijnen buiten naamruimten, b.v.

Men kan stylecop aanpassen om te controleren using richtlijnen buiten naamruimten door toevoegen stylecop.json bestand in de root van het projectbestand met het volgende:

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

U kunt dit configuratiebestand op oplossingsniveau maken en dit aan uw projecten toevoegen als 'Bestaand Link-bestand' om de config ook in al uw projecten te delen.


0
2018-06-03 12:38



Het is een betere oefening als die standaard gebruik d.w.z.referenties"gebruikt in uw bronoplossing zou buiten de namespaces moeten zijn en degene die dat wel zijn "nieuwe toegevoegde referentie" is een goede gewoonte om het in de naamruimte te plaatsen. Dit is om te onderscheiden welke referenties worden toegevoegd.


-7
2017-10-14 21:30