Używam StyleCop na pewnym kodzie C # i ciągle zgłasza, że moje using
dyrektywy powinny znajdować się w przestrzeni nazw.
Czy istnieje techniczny powód, aby umieścić using
dyrektywy w środku zamiast poza przestrzenią nazw?
c#
.net
namespaces
stylecop
code-organization
benPearce
źródło
źródło
using
stwierdzenia ; to sąusing
dyrektywy . Zusing
drugiej strony, instrukcja jest strukturą językową, która występuje wraz z innymi instrukcjami wewnątrz ciała metody itp. Na przykład,using (var e = s.GetEnumerator()) { /* ... */ }
instrukcja jest luźno taka sama jakvar e = s.GetEnumerator(); try { /* ... */ } finally { if (e != null) { e.Dispose(); } }
.using
instrukcji wnamespace
deklaracjach, w swoich wewnętrznychOdpowiedzi:
W rzeczywistości istnieje (subtelna) różnica między nimi. Wyobraź sobie, że masz następujący kod w pliku File1.cs:
Teraz wyobraź sobie, że ktoś dodaje do projektu kolejny plik (File2.cs), który wygląda następująco:
Kompilator wyszukuje
Outer
przed spojrzeniem na teusing
dyrektywy poza przestrzenią nazw, więc znajdujeOuter.Math
zamiastSystem.Math
. Niestety (a może na szczęście?) NieOuter.Math
maPI
członka, więc File1 jest teraz zepsuty.Zmienia się to, jeśli umieścisz
using
wewnątrz deklaracji przestrzeni nazw, co następuje:Teraz kompilator wyszukuje
System
przed wyszukiwaniemOuter
, znajdujeSystem.Math
i wszystko jest w porządku.Niektórzy twierdzą, że
Math
może to być zła nazwa dla klasy zdefiniowanej przez użytkownika, ponieważ jest już w niej jednaSystem
; Chodzi o to tylko, że nie ma różnicy, a to wpływa na łatwość konserwacji swojego kodu.Interesujące jest również odnotowanie, co się stanie, jeśli
Foo
będzie w przestrzeni nazwOuter
, a nieOuter.Inner
. W takim przypadku dodanieOuter.Math
do pliku 2 powoduje uszkodzenie pliku 1 bez względu na to, dokądusing
idzie. Oznacza to, że kompilator przeszukuje najbardziej wewnętrzną obejmującą przestrzeń nazw, zanim zajmie się jakąkolwiekusing
dyrektywą.źródło
Wątek ten ma już kilka świetnych odpowiedzi, ale wydaje mi się, że dzięki tej dodatkowej odpowiedzi mogę przedstawić nieco więcej szczegółów.
Najpierw pamiętaj, że deklaracja przestrzeni nazw z kropkami, np .:
jest całkowicie równoważny z:
Jeśli chcesz, możesz zastosować
using
dyrektywy na wszystkich tych poziomach. (Oczywiście chcemy miećusing
tylko w jednym miejscu, ale byłoby to zgodne z prawem w zależności od języka).Regułę rozstrzygania, który typ jest domyślny, można luźno sformułować w następujący sposób: Najpierw wyszukaj najbardziej wewnętrzny „zakres” dopasowania, jeśli nic nie zostanie znalezione, przejdź o jeden poziom do następnego zakresu i wyszukaj tam itd. , aż do znalezienia dopasowania. Jeśli na pewnym poziomie znaleziono więcej niż jedno dopasowanie, jeśli jeden z typów pochodzi z bieżącego zestawu, wybierz go i wyślij ostrzeżenie kompilatora. W przeciwnym razie zrezygnuj (błąd czasu kompilacji).
Wyjaśnijmy teraz, co to oznacza w konkretnym przykładzie z dwoma głównymi konwencjami.
(1) Przy zastosowaniach na zewnątrz:
W powyższym przypadku, aby dowiedzieć się, jaki
Ambiguous
jest typ , wyszukiwanie odbywa się w następującej kolejności:C
(w tym dziedziczone typy zagnieżdżone)MyCorp.TheProduct.SomeModule.Utilities
MyCorp.TheProduct.SomeModule
MyCorp.TheProduct
MyCorp
System
,System.Collections.Generic
,System.Linq
,MyCorp.TheProduct.OtherModule
,MyCorp.TheProduct.OtherModule.Integration
, iThirdParty
Druga konwencja:
(2) Przy użyciu wewnątrz:
Teraz wyszukaj typ
Ambiguous
idzie w następującej kolejności:C
(w tym dziedziczone typy zagnieżdżone)MyCorp.TheProduct.SomeModule.Utilities
System
,System.Collections.Generic
,System.Linq
,MyCorp.TheProduct
,MyCorp.TheProduct.OtherModule
,MyCorp.TheProduct.OtherModule.Integration
, iThirdParty
MyCorp.TheProduct.SomeModule
MyCorp
(Należy pamiętać, że
MyCorp.TheProduct
był on częścią „3.” i dlatego nie był potrzebny między „4.” a „5.”.)Uwagi końcowe
Bez względu na to, czy umieścisz użytkowanie w deklaracji przestrzeni nazw, czy poza nią, zawsze istnieje możliwość, że ktoś później doda nowy typ o identycznej nazwie do jednej z przestrzeni nazw o wyższym priorytecie.
Ponadto, jeśli zagnieżdżona przestrzeń nazw ma taką samą nazwę jak typ, może to powodować problemy.
Przenoszenie zastosowań z jednej lokalizacji do innej zawsze jest niebezpieczne, ponieważ zmienia się hierarchia wyszukiwania i można znaleźć inny typ. Dlatego wybierz jedną konwencję i trzymaj się jej, abyś nigdy nie musiał przenosić usług.
Szablony Visual Studio domyślnie umieszczają zastosowania poza obszarem nazw (na przykład, jeśli VS wygeneruje nową klasę w nowym pliku).
Jedną (niewielką) zaletą korzystania z usług na zewnątrz jest to, że można następnie użyć dyrektyw używania dla atrybutu globalnego, na przykład
[assembly: ComVisible(false)]
zamiast[assembly: System.Runtime.InteropServices.ComVisible(false)]
.źródło
Umieszczenie go w przestrzeniach nazw powoduje, że deklaracje są lokalne dla tej przestrzeni nazw dla pliku (w przypadku, gdy w pliku znajduje się wiele przestrzeni nazw), ale jeśli masz tylko jedną przestrzeń nazw dla pliku, to nie ma znaczenia, czy wychodzą na zewnątrz, czy w przestrzeni nazw.
źródło
using
dyrektywy wnamespace
blokach mogą odnosić się do względnych przestrzeni nazw opartych na otaczającymnamespace
bloku.Według Hanselmana - Korzystanie z dyrektywy i ładowania zespołu ... i innych tego typu artykułów nie ma technicznej różnicy.
Wolę umieszczać je poza przestrzenią nazw.
źródło
My style is to put them outside the namespaces.
- ledwie odpowiedź.Zgodnie z dokumentacją StyleCop:
SA1200: UsingDirectivesMustBePlacedWithinNamespace
Przyczyna AC # za pomocą dyrektywy jest umieszczony poza elementem przestrzeni nazw.
Opis reguły Naruszenie tej reguły występuje, gdy dyrektywa using lub dyrektywa using-alias zostanie umieszczona poza elementem przestrzeni nazw, chyba że plik nie zawiera żadnych elementów przestrzeni nazw.
Na przykład poniższy kod spowodowałby dwa naruszenia tej reguły.
Poniższy kod nie spowoduje jednak naruszenia tej reguły:
Ten kod skompiluje się czysto, bez żadnych błędów kompilatora. Nie jest jednak jasne, która wersja typu Guid jest przydzielana. Jeśli dyrektywa using zostanie przeniesiona do przestrzeni nazw, jak pokazano poniżej, wystąpi błąd kompilatora:
Kod kończy się niepowodzeniem w przypadku następującego błędu kompilatora, znalezionego w wierszu zawierającym
Guid g = new Guid("hello");
CS0576: Przestrzeń nazw „Microsoft.Sample” zawiera definicję sprzeczną z aliasem „Guid”
Kod tworzy alias typu System.Guid o nazwie Guid, a także tworzy własny typ o nazwie Guid z pasującym interfejsem konstruktora. Później kod tworzy instancję typu Guid. Aby utworzyć to wystąpienie, kompilator musi wybrać jedną z dwóch różnych definicji Guid. Kiedy dyrektywa using-alias zostanie umieszczona poza elementem przestrzeni nazw, kompilator wybierze lokalną definicję Guid zdefiniowaną w lokalnej przestrzeni nazw i całkowicie zignoruje dyrektywę using-alias zdefiniowaną poza przestrzenią nazw. To niestety nie jest oczywiste podczas czytania kodu.
Gdy dyrektywa using-alias znajduje się w obszarze nazw, kompilator musi jednak wybierać między dwoma różnymi, sprzecznymi typami Guid, które są zdefiniowane w tej samej przestrzeni nazw. Oba te typy zapewniają zgodnego konstruktora. Kompilator nie może podjąć decyzji, więc oznacza błąd kompilatora.
Umieszczenie dyrektywy using-alias poza przestrzenią nazw jest złą praktyką, ponieważ może prowadzić do nieporozumień w sytuacjach takich jak ta, gdzie nie jest oczywiste, która wersja tego typu jest faktycznie używana. Może to potencjalnie prowadzić do błędu, który może być trudny do zdiagnozowania.
Umieszczenie dyrektyw przy użyciu aliasu w elemencie przestrzeni nazw eliminuje to jako źródło błędów.
Umieszczanie wielu elementów przestrzeni nazw w jednym pliku jest na ogół złym pomysłem, ale jeśli i kiedy to zostanie zrobione, dobrym pomysłem jest umieszczenie wszystkich za pomocą dyrektyw w obrębie każdego z elementów przestrzeni nazw, a nie globalnie na górze pliku. Spowoduje to ścisłe zawężenie przestrzeni nazw, a także pomoże uniknąć opisanych powyżej zachowań.
Należy zauważyć, że kiedy kod został napisany przy użyciu dyrektyw umieszczonych poza przestrzenią nazw, należy zachować ostrożność podczas przenoszenia tych dyrektyw w przestrzeni nazw, aby upewnić się, że nie zmienia to semantyki kodu. Jak wyjaśniono powyżej, umieszczenie dyrektyw przy użyciu aliasu w elemencie przestrzeni nazw umożliwia kompilatorowi wybór między konfliktami typów w sposób, który nie nastąpi, gdy dyrektywy zostaną umieszczone poza przestrzenią nazw.
Jak naprawić naruszenia Aby naprawić naruszenie tej reguły, przenieś wszystkie za pomocą dyrektyw i dyrektyw przy użyciu aliasu w elemencie przestrzeni nazw.
źródło
using
poza przestrzenią nazw. Wewnętrznausing
s wygląda tak brzydki do mnie. :)Występuje problem z umieszczaniem instrukcji przy użyciu w przestrzeni nazw, gdy chcesz używać aliasów. Alias nie korzysta z wcześniejszych
using
instrukcji i musi być w pełni kwalifikowany.Rozważać:
przeciw:
Może to być szczególnie wyraźne, jeśli masz długi alias, taki jak następujący (tak znalazłem problem):
Dzięki
using
instrukcjom w przestrzeni nazw nagle staje się:Nie ładna
źródło
class
potrzeby nazwę (identyfikator). Nie możesz miećusing
dyrektywy wewnątrz klasy, jak to wskazujesz. Musi znajdować się na poziomie przestrzeni nazw, na przykład na zewnątrznamespace
lub na zewnątrznamespace
(ale nie wewnątrz klasy / interfejsu / itp.).using
dyrektywy. Zredagowałem to tak, jak zamierzałem. Dzięki za wskazanie. Rozumowanie jest jednak nadal takie samo.Jak powiedział Jeppe Stig Nielsen , ten wątek ma już świetne odpowiedzi, ale myślałem, że o tej dość oczywistej subtelności warto też wspomnieć.
using
dyrektywy określone w przestrzeniach nazw mogą tworzyć krótszy kod, ponieważ nie muszą być w pełni kwalifikowane, jak wtedy, gdy są określone na zewnątrz.Poniższy przykład działa, ponieważ wszystkie typy
Foo
iBar
to zarówno w tej samej przestrzeni nazw globalnejOuter
.Załóżmy plik kodu Foo.cs :
I Bar.cs :
W
using
skrócie może to pomijać zewnętrzną przestrzeń nazw w dyrektywie:źródło
Jedna zmarszczka, na którą natknąłem się (która nie jest uwzględniona w innych odpowiedziach):
Załóżmy, że masz te przestrzenie nazw:
Kiedy używasz
using Something.Other
poza anamespace Parent
, odnosi się do pierwszego (Coś. Inne).Jeśli jednak użyjesz go w deklaracji przestrzeni nazw, odnosi się do drugiej (Parent.Something.Other)!
Istnieje proste rozwiązanie: dodaj
global::
przedrostek „ ”: docsźródło
Przyczyny techniczne są omówione w odpowiedziach i myślę, że w końcu chodzi o osobiste preferencje, ponieważ różnica nie jest tak duża, a obie są kompromisy. Domyślny szablon programu Visual Studio do tworzenia
.cs
plików korzysta zusing
dyrektyw poza obszarami nazw, npMożna dostosować stylecop, aby sprawdzać
using
dyrektywy poza obszarami nazw, dodającstylecop.json
plik do katalogu głównego pliku projektu w następujący sposób:Możesz utworzyć ten plik konfiguracyjny na poziomie rozwiązania i dodać go do swoich projektów jako „Istniejący plik linku”, aby udostępnić konfigurację również we wszystkich swoich projektach.
źródło
Inna subtelność, która nie wydaje mi się, że została ujęta w innych odpowiedziach, dotyczy sytuacji, gdy masz klasę i przestrzeń nazw o tej samej nazwie.
Kiedy importujesz w przestrzeni nazw, znajdzie klasę. Jeśli import znajduje się poza obszarem nazw, wówczas import zostanie zignorowany, a klasa i przestrzeń nazw muszą być w pełni kwalifikowane.
źródło
Lepszą praktyką jest, aby te domyślne, które używają np. „ Referencji ” używanych w rozwiązaniu źródłowym, znajdowały się poza przestrzeniami nazw, a te, które są „nowymi dodanymi referencjami”, są dobrą praktyką, należy umieszczać je w przestrzeni nazw. Ma to na celu rozróżnienie, jakie odniesienia są dodawane.
źródło