Ar direktyvų naudojimas turėtų būti vardų erdvėje ar už jos ribų?

Bėgau StyleCop per C # kodą, ir jis ir toliau sako, kad mano using direktyvos turi būti vardų erdvėje.

Ar yra techninė priežastis, kodėl įterpiant direktyvas viduje, o ne vardų erdvėje?

1827 m
24 сент. nustatė benPearce rugsėjo 24 d 2008-09-24 06:49 '08 at 6:49 2008-09-24 06:49
@ 10 atsakymų

Iš tiesų, tarp jų yra (subtilus) skirtumas. Įsivaizduokite, kad faile File1.cs turite šį kodą:

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

Įsivaizduokite, kad kažkas pridės kitą failą (File2.cs) į projektą, kuris atrodo taip:

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

Kompiliatorius ieško „ Outer prieš žvelgdamas į tuos, kurie naudoja direktyvas už vardų srities ribų, todėl jis suranda „ Outer.Math o ne „ Outer.Math . Deja (arba, galbūt, laimei?), Outer.Math neturi PI nario, todėl File1 nebeveikia.

Tai pasikeis, jei using pavadinimo erdvės deklaraciją:

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

Dabar kompiliatorius ieško System prieš ieškodamas Outer , suranda System.Math ir viskas gerai.

Kai kurie teigia, kad Math gali būti blogas pasirinktos klasės pavadinimas, nes System ; Faktas yra tas, kad yra skirtumas, ir tai daro įtaką jūsų kodo išlaikymui.

Taip pat įdomu pastebėti, kas atsitiks, jei „ Foo yra Outer vardų erdvėje, o ne Outer.Inner . Tokiu atveju pridedant „ Outer.Math į „ Outer.Math pažeidžiamas Outer.Math , nesvarbu, kur jis using . Tai reiškia, kad kompiliatorius ieško slapčiausios vardų erdvės, kol ji nemato jokios using direktyvos.

1920 m
30 сент. Charlie atsakymas 2008-09-30 05:33 '08 at 5:33 am 2008-09-30 05:33

Jau yra keletas puikių atsakymų į šią temą, bet manau, kad galiu pridėti šiek tiek išsamesnės informacijos apie šį papildomą atsakymą.

Pirma, atminkite, kad vardų srities deklaracija su laikotarpiais, pavyzdžiui:

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

visiškai lygiavertis:

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

Jei norite, galite using direktyvas visais šiais lygiais. (Žinoma, norime using tik vienoje vietoje, tačiau ji bus teisėta pagal kalbą.)

Taisyklė, pagal kurią nustatoma, koks tipas yra numanomas, gali būti laisvai nurodomas taip: Pirma, suraskite pačią giliausią „taikymo sritį“, jei nieko nerasta, eikite vienu lygiu į kitą sritį ir ieškokite ten ir pan. sutapimas. Jei bet kuriame lygyje randama daugiau nei viena atitiktis, jei vienas iš tipų yra iš dabartinės surinkimo, pasirinkite jį ir išduokite kompiliatoriaus įspėjimą. Priešingu atveju, atleiskite (kompiliavimo laiko klaida).

Dabar atkreipkime dėmesį į tai, ką tai reiškia konkrečiame pavyzdyje su dviem pagrindinėmis konvencijomis.

(1) Naudojant:

 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 atveju, norėdami sužinoti, kokio tipo Ambiguous , paieška vyksta tokia tvarka:

  • Įdėtos rūšys C viduje (įskaitant paveldėtus įdėtus tipus)
  • Dabartinės vardų erdvės MyCorp.TheProduct.SomeModule.Utilities
  • MyCorp.TheProduct.SomeModule tipų tipai
  • MyCorp.TheProduct tipai
  • MyCorp tipai
  • Tipai tuščioje vardų erdvėje (pasaulinė vardų erdvė)
  • Tipai System , System.Collections.Generic , System.Linq , MyCorp.TheProduct.OtherModule , MyCorp.TheProduct.OtherModule.Integration ir ThirdParty
border=0

Kita sutartis:

(2) Naudojant viduje:

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

Dabar ieškokite Ambiguous tipo tokia tvarka:

  • Įdėtos rūšys C viduje (įskaitant paveldėtus įdėtus tipus)
  • Dabartinės vardų erdvės MyCorp.TheProduct.SomeModule.Utilities
  • Tipai System , System.Collections.Generic , System.Linq , „ MyCorp.TheProduct , „ MyCorp.TheProduct.OtherModule , „ MyCorp.TheProduct.OtherModule.Integration and ThirdParty
  • MyCorp.TheProduct.SomeModule tipų tipai
  • MyCorp tipai
  • Tipai tuščioje vardų erdvėje (pasaulinė vardų erdvė)

(Atkreipkite dėmesį, kad „ MyCorp.TheProduct yra „3.“ dalis, todėl nereikalaujama tarp „4.“ ir „5.“.)

Baigiamosios pastabos

Nepriklausomai nuo to, ar įdėjote pranešimus vardų erdvės deklaracijos viduje ar už jos ribų, visada yra tikimybė, kad kažkas vėliau prideda naują tipą su tuo pačiu pavadinimu į vieną iš aukštesniojo prioriteto vardų.

Be to, jei įdėtos vardų vietos pavadinimas yra toks pat, kaip ir tipas, tai gali sukelti problemų.

Visada pavojinga perkelti duomenis iš vienos vietos į kitą, nes pasikeičia paieškos hierarchija, ir galima rasti kitą tipą. Taigi pasirinkite vieną susitarimą ir laikykitės jo, kad niekada neturėtumėte judėti.

„Visual Studio“ šablonai pagal nutylėjimą pateikia duomenis už vardų srities ribų (pavyzdžiui, jei sukuriate „VS“, sukurkite naują klasę naujame faile).

Vienas (mažas) pranašumas naudojant išorinius įrenginius yra tai, kad galite naudoti naudojimosi direktyvas visuotiniam atributui, pvz., [assembly: ComVisible(false)] vietoj [assembly: System.Runtime.InteropServices.ComVisible(false)] .

374
19 апр. Jeppe Stig Nielsen atsakymas 19 d 2013-04-19 00:00 '13, 0:00 2013-04-19 00:00

Vardų erdvėse patalpinimas leidžia vietinei vietai pateikti failo vardų erdvę (jei faile yra kelios vardų vietos), tačiau jei kiekvienam failui yra tik viena vardų erdvė, nesvarbu, ar jie yra, ar ne viduje vardų erdvėje.

 using ThisNamespace.IsImported.InAllNamespaces.Here; namespace Namespace1 { using ThisNamespace.IsImported.InNamespace1.AndNamespace2; namespace Namespace2 { using ThisNamespace.IsImported.InJustNamespace2; } } namespace Namespace3 { using ThisNamespace.IsImported.InJustNamespace3; } 
187
24 сент. Mark Cidade atsakymas, pateiktas rugsėjo 24 d. 2008-09-24 06:52 '08 at 6:52 am 2008-09-24 06:52

Pagal Hanselmaną, naudojant direktyvas ir rinkinius, pakrovimas ... ir kiti panašūs dirbiniai techniškai nėra skirtingi.

Mano pirmenybė teikiama tam, kad jie būtų patalpinti ne vardų erdvėse.

58
24 сент. Quintin Robinson atsakymas, pateiktas rugsėjo 24 d. 2008-09-24 06:53 '08, 6:53 am. 2008-09-24 06:53

Pagal „StyleCop“ dokumentaciją:

SA1200: naudojantDirectivesMustBePlacedWithinNamespace

Priežastis C # direktyvos direktyva yra už vardų elemento.

Taisyklės aprašymas Šios taisyklės pažeidimas atsiranda tada, kai naudojant direktyvos arba naudojimosi slapyvardžio direktyvą yra už vardų elemento elemento, nebent faile yra vardų elementų.

Pavyzdžiui, šis kodas sukels du šios taisyklės pažeidimus.

 using System; using Guid = System.Guid; namespace Microsoft.Sample { public class Program { } } 

Tačiau toliau nurodytas kodas nepažeidžia šios taisyklės:

 namespace Microsoft.Sample { using System; using Guid = System.Guid; public class Program { } } 

Šis kodas bus sudarytas be kompiliatoriaus klaidų. Tačiau neaišku, kuri Guid tipo versija išsiskiria. Jei naudojimo direktyva perkeliama vardų srityje, kaip parodyta toliau, atsiras kompiliatoriaus klaida:

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

Kodas nepavyksta su tokia kompiliatoriaus klaida, kuri randama eilutėje, kurioje yra Guid g = new Guid("hello");

CS0576: „Microsoft.Sample“ vardų erdvėje yra apibrėžimas, prieštaraujantis „Guid“ slapyvardžiui

Kodas sukuria „System.Guid“ tipo pavadinimą, vadinamą „Guid“, taip pat sukuria savo tipą „Guid“ su tinkama dizaino sąsaja. Vėliau kodas sukuria Guid tipo pavyzdį. Norėdami sukurti šį atvejį, kompiliatorius turi pasirinkti dvi skirtingas „Guid“ apibrėžtis. Kai naudojimosi slapyvardžio direktyva yra už vardų elemento elemento, kompiliatorius pasirinks vietinę pavadinimo erdvėje apibrėžtą apibrėžtį „Guid“ ir visiškai ignoruos direktyvą, naudodama-alias, apibrėžtą už vardų srities ribų. Tai, deja, nėra aišku skaitant kodą.

Tačiau, kai naudojimosi-alias direktyva yra patalpinta vardų erdvėje, kompiliatorius turi pasirinkti iš dviejų skirtingų prieštaringų Guid tipų, kurie yra apibrėžti toje pačioje vardų erdvėje. Abu šie tipai suteikia atitikties konstruktorių. Kompiliatorius negali priimti sprendimo, todėl nurodo kompiliatoriaus klaidą.

Naudojimo aliaso direktyvos pateikimas ne vardų erdvėje yra bloga praktika, nes ji gali būti paini situacijose, kai nėra aišku, kokia tipo versija iš tikrųjų naudojama. Tai gali sukelti klaidą, kurią gali būti sunku diagnozuoti.

Nukreipiant direktyvas naudojant slapyvardžius vardų erdvėje, tai pašalinama kaip klaidų šaltinis.

  1. Keli pavadinimo plotai

Kelių vardų elementų įterpimas į vieną failą paprastai yra bloga idėja, tačiau jei tai daroma, rekomenduojama, kad visos naudodamos direktyvas įterptų kiekvienoje vardų erdvės dalyje, o ne visame pasaulyje failo viršuje. Tai labai paveiks vardų vietas ir padės išvengti pirmiau aprašyto elgesio.

Svarbu pažymėti, kad kai kodas buvo parašytas naudojant direktyvų, esančių už vardų srities ribų, atsargumą, reikia, kad šios direktyvos būtų perkeltos į vardų sritį, kad būtų užtikrinta, jog tai nepakeis kodo semantikos. Kaip paaiškinta pirmiau, direktyvų, kuriose slapyvardžiai naudojami, įvedimas į vardų elementą leidžia kompiliatoriui pasirinkti tarp prieštaraujančių tipų būdais, kurie nebus vykdomi, kai direktyvos bus pateiktos už vardų srities ribų.

Pažeidimų taisymas Norėdami pašalinti šios taisyklės pažeidimą, perkelkite viską naudodamiesi direktyvų ir slapyvardžių direktyvomis vardų elemento elemente.

46
14 сент. Atsakymą pateikė JaredCacurak 14 sep . 2009-09-14 18:17 '09, 18:17 PM 2009-09-14 18:17

Jei norite naudoti slapyvardžius, problema kyla dėl to, kad naudodami vardų sritį veikiantys operatoriai. Slapyvardis nesinaudoja ankstesniais pareiškimais ir turi būti visiškai kvalifikuotas.

Apsvarstykite:

 namespace MyNamespace { using System; using MyAlias = System.DateTime; class MyClass { } } 

prieš

 using System; namespace MyNamespace { using MyAlias = DateTime; class MyClass { } } 

Tai gali būti ypač ryškus, jei turite ilgą slapyvardį, pvz., Toliau pateiktą informaciją (čia aptinku problemą):

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

Naudojant pareiškimus vardų erdvėje, jis staiga tampa:

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

Ne iš tikrųjų.

31
10 окт. Atsakymas pateikiamas Neo 10 okt. 2012-10-10 21:47 '12 9:47 val. 2012-10-10 21:47

Kaip sakė Jeppe Stig Nielsen, ši gija jau turi puikių atsakymų, bet aš maniau, kad tai gana akivaizdus subtilumas taip pat verta paminėti.

Vardų erdvėse nurodytų direktyvų naudojimas gali sudaryti trumpesnį kodą, nes jie nebūtinai turi būti visiškai kvalifikuoti, kai jie nurodyti išorėje.

Toliau pateiktas pavyzdys veikia, nes tipai „ Foo ir „ Bar yra toje pačioje pasaulio vardų erdvėje „ Outer .

Tarkime, kad kodo failas Foo.cs:

 namespace Outer.Inner { class Foo { } } 

Ir Bar.cs:

 namespace Outer { using Outer.Inner; class Bar { public Foo foo; } } 

Tai gali praleisti išorinę vardų erdvę using direktyvoje, trumpai:

 namespace Outer { using Inner; class Bar { public Foo foo; } } 
3
17 сент. Atsakymas pateikiamas sausainiams 17 sep. 2016-09-17 13:32 '16, 13:32 pm 2016-09-17 13:32

Kitas subtilumas, kurio aš nemanau, buvo įtrauktas į kitus atvejus, kai turite tą patį pavadinimą turinčią klasės ir vardų erdvę.

Kai importuojate vardų erdvę, ji suras klasę. Jei importas yra ne vardų srityje, importas bus ignoruojamas, o klasės ir vardų erdvė turi būti visiškai apibrėžta.

 //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. } } } 
1
24 авг. Ben Gardner atsakymas rugpjūčio 24 d 2018-08-24 18:28 '18 18:28 2018-08-24 18:28

Atsakymuose aptariamos techninės priežastys, ir manau, kad galiausiai tai susiję su asmeninėmis nuostatomis, nes skirtumas nėra toks didelis, ir abiem yra kompromisų. Pavyzdžiui, numatytasis „Visual Studio“ šablonas, sukuriantis .cs failus, using direktyvas už vardų erdvių ribų

Galite sukonfigūruoti „stylecop“, kad patikrintumėte using direktyvomis už vardų erdvių ribų, pridėdami „ stylecop.json failą į projekto failo šaknį, naudodami:

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

Šį konfigūracijos failą galite sukurti sprendimo lygmeniu ir pridėti jį prie savo projektų kaip „Esamos nuorodos failą“, kad galėtumėte bendrinti konfigūraciją su visais savo projektais.

0
03 июня '18 в 15:38 2018-06-03 15:38 atsakymas pateikiamas birželio 18 d. 18 val. 15:38 2018-06-03 15:38

Tai geriau, jei numatytieji , ty „nuorodos“, naudojami pradiniame sprendime, turėtų būti ne vardų erdvėse, o tie, kurie yra „nauja nuoroda“ , yra gera praktika, kad ją įdėjote į vardų sritį . Tai reiškia, kad kokios nuorodos pridedamos.

-8
15 окт. Izraelio atsakymas Ocbina Oct 15 2014-10-15 00:30 '14 - 0:30 2014-10-15 00:30

Kiti klausimai, susiję su „ žymėmis arba užduoti klausimą