Frage

Ich bin gerannt StyleCop über etwas C#-Code, und es wird immer wieder gemeldet, dass mein using Direktiven sollten sich innerhalb des Namespace befinden.

Gibt es einen technischen Grund für das Setzen? using Direktiven innerhalb statt außerhalb des Namespace?

War es hilfreich?

Lösung

Es gibt tatsächlich eine (subtilen) Unterschied zwischen den beiden. Stellen Sie sich vor Sie den folgenden Code in File1.cs haben:

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

Nun stell dir vor, dass jemand eine andere Datei (File2.cs) zu dem Projekt hinzufügt, die wie folgt aussieht:

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

Der Compiler sucht Outer vor außerhalb des Namespace an diesen using Richtlinien suchen, so dass es findet statt Outer.Math System.Math. Leider (oder vielleicht zum Glück?), Outer.Math kein PI Mitglied hat, so File1 ist nun gebrochen.

Dies ändert sich, wenn Sie die using in Ihrem Namensraum-Deklaration setzen, wie folgt:

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

Jetzt System der Compiler sucht vor Outer sucht, findet System.Math, und alles ist gut.

Einige würden argumentieren, dass Math könnte ein schlechter Name für eine benutzerdefinierte Klasse sein, da es bereits eine in System; Der Punkt hier ist nur, dass es ist ein Unterschied, und es wirkt sich auf die Wartbarkeit des Codes.

Es ist auch interessant zu sehen, was passiert, wenn Foo im Namensraum Outer, anstatt Outer.Inner. In diesem Fall das Hinzufügen Outer.Math in File2 bricht File1 unabhängig davon, wo die using geht. Dies bedeutet, dass der Compiler den innersten einschließenden Namespace sucht, bevor es zu jeder using Richtlinie sieht.

Andere Tipps

Dieser Thread hat bereits einige großen Antworten, aber ich fühle ich mich ein wenig näher mit dieser zusätzlichen Antwort bringen kann.

Zunächst erinnern, dass eine Namespace-Deklaration mit Perioden, wie:

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

ist vollständig äquivalent zu:

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

Wenn Sie wollten, könnten Sie using Richtlinien auf allen diesen Ebenen setzen. (Natürlich wollen wir usings in nur einem Ort haben, aber es wäre nach der Sprache legal.)

Die Regel für die Lösung, welche Art impliziert wird, kann locker wie folgt angegeben werden: First-Suche am weitesten innen „scope“ für ein Spiel, wenn nichts da eine Ebene gefunden wird auf den nächsten Rahmen hinausgehen und Suche dort, und so weiter , bis eine Übereinstimmung gefunden wird. Wenn auf einem gewissen Ebene mehr als eine Übereinstimmung gefunden wird, wenn einer der Typen aus der aktuellen Baugruppe ist, dass man wählen, und eine Compiler-Warnung ausgeben. Andernfalls geben (Kompilierzeitfehler).

Lassen Sie uns nun über explizit sein, was das bedeutet in einem konkreten Beispiel mit den beiden großen Konventionen.

Mit

(1) usings außen:

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

Im obigen Fall, um herauszufinden, welche Art Ambiguous ist, geht die Suche in dieser Reihenfolge:

  1. Verschachtelte Typen innerhalb C (einschließlich geerbt verschachtelte Typen)
  2. Typen im aktuellen Namensraum MyCorp.TheProduct.SomeModule.Utilities
  3. Typen im Namensraum MyCorp.TheProduct.SomeModule
  4. Typen in MyCorp.TheProduct
  5. Typen in MyCorp
  6. Typen im null Namensraum (der globalen Namespace)
  7. Typen in System, System.Collections.Generic, System.Linq, MyCorp.TheProduct.OtherModule, MyCorp.TheProduct.OtherModule.Integration und ThirdParty

Die andere Konvention:

(2) Mit usings innen:

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

Nun, die Suche nach dem Typ Ambiguous geht in dieser Reihenfolge:

  1. Verschachtelte Typen innerhalb C (einschließlich geerbt verschachtelte Typen)
  2. Typen im aktuellen Namensraum MyCorp.TheProduct.SomeModule.Utilities
  3. Typen in System, System.Collections.Generic, System.Linq, MyCorp.TheProduct, MyCorp.TheProduct.OtherModule, MyCorp.TheProduct.OtherModule.Integration und ThirdParty
  4. Typen im Namensraum MyCorp.TheProduct.SomeModule
  5. Typen in MyCorp
  6. Typen im null Namensraum (der globalen Namespace)

(Beachten Sie, dass MyCorp.TheProduct ein Teil von „3“ war und daher nicht benötigt, um zwischen „4“ und „5“.).

Abschließende Bemerkungen

Egal, ob Sie die usings innerhalb oder außerhalb der Namespace-Deklaration setzen, gibt es immer die Möglichkeit, dass jemand später einen neuen Typ mit identischen Namen einer der Namensräume ergänzt, die eine höhere Priorität haben.

Auch wenn eine verschachtelte Namespace die gleichen Namen wie eine Art hat, kann es zu Problemen führen.

Es ist immer gefährlich, die usings von einem Ort zum anderen zu bewegen, weil die Suchhierarchie ändert, und eine andere Art gefunden werden kann. Daher ist es eine Konvention wählen und dabei bleiben, so dass Sie nicht immer müssen usings bewegen.

Visual Studio-Vorlagen, die standardmäßig setzten die usings außerhalb des Namensraumes (zum Beispiel, wenn Sie machen VS eine neue Klasse in einer neuen Datei erzeugen).

Ein (kleiner) Vorteil, usings außerhalb ist, dass man dann die Verwendung von Richtlinien für ein globales Attribut nutzen kann, zum Beispiel [assembly: ComVisible(false)] statt [assembly: System.Runtime.InteropServices.ComVisible(false)].

Um es in den Namensräumen macht die Erklärungen lokal für diesen Namespace für die Datei (falls Sie mehrere Namespaces in der Datei haben), aber wenn Sie nur einen Namensraum pro Datei haben, dann ist es nicht viel Unterschied machen, ob sie gehen außerhalb oder innerhalb des Namespace.

using ThisNamespace.IsImported.InAllNamespaces.Here;

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

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

namespace Namespace3
{ 
   using ThisNamespace.IsImported.InJustNamespace3;
}

Nach Hanselman - Mit der Richtlinie und Montage Laden ... und andere Artikel gibt es technisch keinen Unterschied.

Meine Präferenz ist, sie außerhalb von Namensräumen zu setzen.

Laut StyleCop-Dokumentation:

SA1200:UsingDirectivesMustBePlacedWithinNamespace

Ursache Wechselstrom# Die Verwendung von Anweisungen wird außerhalb eines Namespace -Elements platziert.

Regelbeschreibung Ein Verstoß gegen diese Regel erfolgt, wenn eine Verwendung von Anweisungen oder eine Verwendung von Alias-Anweisungen außerhalb eines Namespace-Elements platziert wird, es sei denn, die Datei enthält keine Namespace-Elemente.

Der folgende Code würde beispielsweise zu zwei Verstößen gegen diese Regel führen.

using System;
using Guid = System.Guid;

namespace Microsoft.Sample
{
    public class Program
    {
    }
}

Der folgende Code würde jedoch zu keinen Verstößen gegen diese Regel führen:

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

    public class Program
    {
    }
}

Dieser Code wird sauber und ohne Compilerfehler kompiliert.Es ist jedoch unklar, welche Version des Guid-Typs zugewiesen wird.Wenn die using-Direktive wie unten gezeigt in den Namespace verschoben wird, tritt ein Compilerfehler auf:

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

Der Code schlägt aufgrund des folgenden Compilerfehlers fehl, der in der Zeile mit gefunden wird Guid g = new Guid("hello");

CS0576:Der Namespace „Microsoft.Sample“ enthält eine Definition, die mit dem Alias ​​„Guid“ in Konflikt steht.

Der Code erstellt einen Alias ​​für den System.Guid-Typ namens Guid und erstellt außerdem einen eigenen Typ namens Guid mit einer passenden Konstruktorschnittstelle.Später erstellt der Code eine Instanz vom Typ Guid.Um diese Instanz zu erstellen, muss der Compiler zwischen den beiden unterschiedlichen Definitionen von Guid wählen.Wenn die using-alias-Direktive außerhalb des Namespace-Elements platziert wird, wählt der Compiler die lokale Definition von Guid, die innerhalb des lokalen Namespace definiert ist, und ignoriert vollständig die using-alias-Direktive, die außerhalb des Namespace definiert ist.Dies ist beim Lesen des Codes leider nicht ersichtlich.

Wenn die using-alias-Direktive jedoch innerhalb des Namespace positioniert ist, muss der Compiler zwischen zwei verschiedenen, widersprüchlichen Guid-Typen wählen, die beide im selben Namespace definiert sind.Beide Typen stellen einen passenden Konstruktor bereit.Der Compiler kann keine Entscheidung treffen und meldet daher den Compilerfehler.

Das Platzieren der using-alias-Direktive außerhalb des Namensraums ist eine schlechte Vorgehensweise, da es in Situationen wie dieser, in denen nicht offensichtlich ist, welche Version des Typs tatsächlich verwendet wird, zu Verwirrung führen kann.Dies kann möglicherweise zu einem Fehler führen, der möglicherweise schwer zu diagnostizieren ist.

Durch die Platzierung von using-alias-Direktiven innerhalb des Namespace-Elements wird dies als Fehlerquelle eliminiert.

  1. Mehrere Namespaces

Das Platzieren mehrerer Namespace-Elemente in einer einzigen Datei ist im Allgemeinen eine schlechte Idee, aber wenn und wann dies geschieht, ist es eine gute Idee, alle using-Direktiven in jedem der Namespace-Elemente zu platzieren und nicht global am Anfang der Datei.Dadurch werden die Namespaces eng begrenzt und es wird auch dazu beitragen, das oben beschriebene Verhalten zu vermeiden.

Es ist wichtig zu beachten, dass beim Schreiben von Code mit using-Direktiven außerhalb des Namensraums beim Verschieben dieser Direktiven innerhalb des Namensraums Vorsicht geboten ist, um sicherzustellen, dass dadurch die Semantik des Codes nicht verändert wird.Wie oben erläutert, ermöglicht die Platzierung von using-alias-Direktiven innerhalb des Namespace-Elements dem Compiler die Auswahl zwischen widersprüchlichen Typen auf eine Weise, die nicht der Fall ist, wenn die Direktiven außerhalb des Namespace platziert werden.

So beheben Sie Verstöße zur Behebung eines Verstoßes gegen diese Regel, verschieben Sie alle mit Richtlinien und verwenden Sie Alias-Richtlinien innerhalb des Namespace-Elements.

Es gibt ein Problem beim Platzieren von using-Anweisungen innerhalb des Namespace, wenn Sie Aliase verwenden möchten.Der Alias ​​profitiert nicht von dem früheren using Aussagen und muss vollständig qualifiziert sein.

Halten:

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

    class MyClass
    {
    }
}

gegen:

using System;

namespace MyNamespace
{
    using MyAlias = DateTime;

    class MyClass
    {
    }
}

Dies kann besonders ausgeprägt sein, wenn Sie einen langatmigen Alias ​​wie den folgenden haben (wobei ich das Problem gefunden habe):

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

Mit using Anweisungen innerhalb des Namensraums, es wird plötzlich:

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

Nicht hübsch.

Wie Jeppe Stig Nielsen sagte dieses Thema bereits große Antworten, aber ich dachte, das ziemlich offensichtlich Subtilität war erwähnenswert zu.

using Richtlinien innerhalb Namensraumes angegeben wird, können für kürzeren Code machen, da sie nicht brauchen, voll qualifiziert zu sein, als wenn sie auf der Außenseite angegeben sind.

Das folgende Beispiel funktioniert, weil die Typen Foo und Bar sind beide in dem gleichen globalen Namensraum, Outer.

Nehmen wir den Code-Datei Foo.cs :

namespace Outer.Inner
{
    class Foo { }
}

und Bar.cs :

namespace Outer
{
    using Outer.Inner;

    class Bar
    {
        public Foo foo;
    }
}

Das kann den äußeren Namespace in der using Richtlinie, kurz auslassen:

namespace Outer
{
    using Inner;

    class Bar
    {
        public Foo foo;
    }
}

Ein Falten Ich lief in (die in anderen Antworten nicht aufgeführt):

Angenommen, Sie haben diese Namensräume:

  • Something.Other
  • Parent.Something.Other

Wenn Sie using Something.Other verwenden außerhalb eines namespace Parent, es bezieht sich auf die erste (Something.Other).

Allerdings, wenn Sie es verwenden innerhalb dieser Namespace-Deklaration, verweist sie auf die zweite (Parent.Something.Other)!

Es gibt eine einfache Lösung: fügen Sie den "global::" Präfix: docs

namespace Parent
{
   using global::Something.Other;
   // etc
}

Eine weitere Raffinesse, das glaube ich nicht von den anderen Antworten bedeckt worden ist, ist, wenn Sie eine Klasse und Namespace mit dem gleichen Namen haben.

Wenn Sie den Import in dem Namespace haben, dann wird es die Klasse finden. Wenn der Import außerhalb des Namensraumes ist, dann wird der Import ignoriert und die Klasse und Namespace müssen vollständig qualifiziert sein.

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

Die technischen Gründe in den Antworten diskutiert werden und ich denke, dass es auf die persönlichen Vorlieben am Ende kommt, da der Unterschied nicht so ist groß und es gibt Vor- und Nachteile für beide. Visual Studio Standard-Vorlage für die Erstellung von .cs Dateien verwenden using Richtlinien außerhalb von Namensräumen z.

Man kann StyleCop anpassen using Richtlinien außerhalb von Namensräumen durch das Hinzufügen stylecop.json Datei im Stamm der Projektdatei mit folgendem zu überprüfen:

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

Sie können diese Konfigurationsdatei in Lösung Ebene erstellen und auf Ihre Projekte als ‚bestehende Link-Datei‘ hinzufügen zu den Config für alle Ihre Projekte zu teilen.

Es ist eine bessere Praxis, wenn diejenigen, Standard mit zB „ Referenzen “ in Ihrer Source-Lösung verwendet, die außerhalb der Namensräume sein sollte, und jene, die "neu hinzugefügt Referenz " ist eine gute Praxis ist, dass Sie es in dem Namespace setzen sollten. Dies ist zu unterscheiden, welche Referenzen hinzugefügt werden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top