Przestrzeń nazw i klasa o tej samej nazwie?

98

Organizuję projekt biblioteki i mam nazwaną centralną klasę menedżera Scenegraphoraz całą masę innych klas, które żyją w przestrzeni nazw Scenegraph.

Naprawdę chciałbym, żeby scenegraph był, MyLib.Scenegrapha inne klasy były MyLib.Scenegraph.*, ale wydaje się, że jedynym sposobem na to byłoby uczynienie wszystkich innych klas wewnętrznymi Scenegraphw pliku Scenegraph.cs, a to jest po prostu zbyt nieporęczne .

Zamiast tego zorganizowałem to jako Mylib.Scenegraph.Scenegraphi MyLib.Scenegraph.*, który rodzaj działa, ale uważam, że Visual Studio jest zdezorientowany w pewnych warunkach, czy mam na myśli klasę, czy przestrzeń nazw.

Czy istnieje dobry sposób na zorganizowanie tego pakietu, tak aby był wygodny dla użytkowników, bez konieczności umieszczania całego kodu razem w niemożliwym do utrzymania bałaganie?

user430788
źródło

Odpowiedzi:

111

Nie polecam nazywać klasy, takiej jak jej przestrzeń nazw, zobacz ten artykuł .

Wytyczne dotyczące projektowania ram mówią w sekcji 3.4 „Nie używaj tej samej nazwy dla przestrzeni nazw i typu w tej przestrzeni nazw”. To jest:

namespace MyContainers.List 
{ 
    public class List { … } 
}

Dlaczego jest to zło? Och, pozwól mi policzyć sposoby.

Możesz znaleźć się w sytuacjach, w których myślisz, że odnosisz się do jednej rzeczy, ale w rzeczywistości odnosisz się do czegoś innego. Załóżmy, że znajdziesz się w tej niefortunnej sytuacji: piszesz Blah.DLL i importujesz Foo.DLL i Bar.DLL, które niestety mają typ o nazwie Foo:

// Foo.DLL: 
namespace Foo { public class Foo { } }

// Bar.DLL: 
namespace Bar { public class Foo { } }

// Blah.DLL: 
namespace Blah  
{   
using Foo;   
using Bar;   
class C { Foo foo; } 
}

Kompilator zgłasza błąd. „Foo” jest niejednoznaczne między Foo.Foo i Bar.Foo. Porażka. Myślę, że naprawię to, w pełni kwalifikując nazwę:

   class C { Foo.Foo foo; } 

To teraz daje błąd niejednoznaczności „ Foo w Foo.Foo jest niejednoznaczne między Foo.Foo i Bar.Foo ”. Nadal nie wiemy, do czego odnosi się pierwsze Foo i dopóki nie uda nam się tego zrozumieć, nawet nie próbujemy dowiedzieć się, do czego odnosi się drugie.

Pinckerman
źródło
6
To interesujący punkt. Wciąż się nad tym zastanawiam. Dzięki. Muszę być szczery, argumenty zaczynające się od „Przewodnik po stylu mówi” nie robią na mnie wrażenia. Widziałem mnóstwo nieuzasadnionych bzdur w przewodnikach po stylu. Ale powyżej przedstawisz dobry praktyczny argument. W każdym razie warto o tym pomyśleć.
user430788
6
Odpowiedź mówi „czego nie robić”. Podczas gdy niektórzy użytkownicy stosu szukają „co robić”. Czy ktoś poleciłby odpowiedź Ant_222?
fantastyczne
3
Jeśli naprawdę utkniesz, nadal możesz utworzyć alias typu poza przestrzenią nazw. Aliasy zewnętrzne są potrzebne tylko wtedy, gdy masz dwa całkowicie identyczne pełne identyfikatory (przestrzeń nazw + nazwa klasy).
Geoffrey,
4
Wszystko to jest do widzenia i opinii. Faktem jest, że nie należy to zrobić, ponieważ kompilator C # będzie go źle zrozumieć , mimo że powiedziano wyraźnie, co się dzieje. Np. using Foo.Bar;Później Bar bdość wyraźnie odnosi się do Barklasy (lub wyliczenia) wewnątrz przestrzeni nazw Foo.Bar. Prawdziwym powodem, aby tego nie robić, jest po prostu to, że kompilator C # nie potrafi tego poprawnie zrozumieć, co jest strasznie zaskakujące i niefortunne. Wszystko inne to porady dotyczące wełnianego stylu.
TJ Crowder
2
Nie rozumiem. Dlaczego Foo.Foo jest niejednoznaczne? Bezpośrednio mówisz kompilatorowi, że w tym konkretnym przypadku chcesz użyć klasy Foo z przestrzeni nazw Foo. Nie?
GuardianX
14

Nadanie tej samej nazwy przestrzeni nazw i klasie może zmylić kompilator, jak powiedzieli inni.

Jak więc to nazwać?

Jeśli przestrzeń nazw ma wiele klas, znajdź nazwę, która definiuje wszystkie te klasy.

Jeśli przestrzeń nazw ma tylko jedną klasę (i stąd pokusa nadania jej tej samej nazwy), nazwij ją ClassName NS . Tak przynajmniej Microsoft nazywa swoje przestrzenie nazw.

Iść do
źródło
5
Czy masz przykład takiej przestrzeni nazw firmy Microsoft?
sunefred
@sunefred Szukałem go, ale nie mogłem go znaleźć. Ale zdecydowanie pamiętam, że widziałem to w dokumentacji. Wydaje mi się, że nie ma wielu przypadków, w których chcesz mieć jedną klasę w przestrzeni nazw.
Przejdź do
9

Proponuję, aby postępować zgodnie z zaleceniami wsiadłem microsoft.public.dotnet.languages.csharpdo użytku MyLib.ScenegraphUtil.Scenegraphi MyLib.ScenegraphUtil.*.

Ant_222
źródło
7

CA1724: Type Names Should Not Match Namespaces ...

Zasadniczo, jeśli zastosujesz się do analizy kodu w celu prawidłowego kodowania, ta reguła mówi, aby nie robić tego, co próbujesz zrobić. Analiza kodu jest bardzo przydatna, pomagając znaleźć potencjalne problemy.

myermian
źródło
4

Dodam tylko moje 2 centy:

Miałem następujące zajęcia:

namespace Foo {
    public struct Bar {
    }
    public class Foo {
        //no method or member named "Bar"
    }
}

Klient został napisany w ten sposób:

using Foo;

public class Blah {
    public void GetFoo( out Foo.Bar[] barArray ) {
    }
}

Wybaczając błąd, że GetFoo nie zwraca danych wyjściowych zamiast użyć parametru out, kompilator nie mógł rozpoznać typu danych Foo.Bar []. Zwracał błąd: nie można znaleźć typu lub przestrzeni nazw Foo.Bar.

Wygląda na to, że przy próbie kompilacji rozwiązał Foo jako klasę i nie znalazł osadzonej klasy Bar w klasie Foo. Nie mógł również znaleźć przestrzeni nazw o nazwie Foo.Bar. Nie udało się wyszukać klasy Bar w przestrzeni nazw Foo. Kropki w przestrzeni nazw NIE są syntaktyczne. Cały ciąg jest tokenem, a nie słowami rozdzielonymi kropkami.

To zachowanie było widoczne w VS 2015 z .Net 4.6

Steve
źródło
4

Chociaż zgadzam się z innymi odpowiedziami, mówiąc, że nie powinieneś nazywać swojej klasy tak samo, jak przestrzeń nazw , są chwile, w których nie możesz spełnić takich wymagań.

Na przykład w moim przypadku to nie ja podejmowałem taką decyzję, dlatego musiałem znaleźć sposób, żeby to zadziałało.

Więc dla tych, którzy nie mogą zmienić nazwy przestrzeni nazw ani nazwy klasy, jest tutaj sposób, w jaki możesz sprawić, by kod działał.

// Foo.DLL: 
namespace Foo { public class Foo { } }

// Bar.DLL: 
namespace Bar { public class Foo { } }

// Blah.DLL: 
namespace Blah
{
    using FooNSAlias = Foo;//alias
    using BarNSAlias = Bar;//alias
    class C { FooNSAlias.Foo foo; }//use alias to fully qualify class name
}

Zasadniczo utworzyłem „aliasy” przestrzeni nazw, co pozwoliło mi w pełni zakwalifikować klasę, a „zamieszanie” w Visual Studio zniknęło.

UWAGA: Powinieneś unikać tego konfliktu nazw, jeśli masz nad tym kontrolę. Powinieneś używać wspomnianej techniki tylko wtedy, gdy nie masz kontroli nad przedmiotowymi klasami i przestrzeniami nazw.

Jonathan Alfaro
źródło
2
Głosowałem za tym, ponieważ było to interesujące rozwiązanie dla niedoskonałego świata.
user430788
3

Jak powiedzieli inni, dobrą praktyką jest unikanie nazywania klasy tak samo, jak jej przestrzeń nazw.

Oto kilka dodatkowych sugestii dotyczących nazewnictwa z odpowiedzi użytkownika svick na pytanie pokrewne „Ta sama klasa i nazwa przestrzeni nazw” na giełdzie Software Engineering Stack Exchange:

Masz rację, nie powinieneś nazywać przestrzeni nazw tak samo, jak typ, który zawiera. Myślę, że jest kilka podejść, których możesz użyć:

  • Pluralize: Model.DataSources.DataSource

Działa to szczególnie dobrze, jeśli głównym celem przestrzeni nazw jest przechowywanie typów, które dziedziczą z tego samego typu podstawowego lub implementują ten sam interfejs.

  • Skróć: Model.QueryStorage

Jeśli przestrzeń nazw zawiera tylko niewielką liczbę typów, być może w ogóle nie potrzebujesz tej przestrzeni nazw.

  • Zrób przedsiębiorczość: Model.ProjectSystem.Project

Może to działać zwłaszcza w przypadku funkcji, które są ważną częścią Twojego produktu, więc zasługują na własne imię.

(Należy pamiętać, że powyższych zastosowań odpowiedź Model.DataSource.DataSource, Model.QueryStorage.QueryStorage.i Model.Project.Projectjako przykłady zamiast MyLib.Scenegraph.Scenegraph).

(Przydały mi się również inne sugestie dotyczące nazewnictwa w innych odpowiedziach).

synek
źródło
2

Stary post, ale oto inny pomysł, który może komuś pomóc:

„… ale wydaje się, że jedynym sposobem na to byłoby uczynienie wszystkich innych klas wewnętrznymi klasami Scenegraph w pliku Scenegraph.cs, a to jest po prostu zbyt nieporęczne”.

To naprawdę lepsza implementacja dla wielu scenariuszy. Ale zgadzam się, że posiadanie całego tego kodu w tym samym pliku .cs jest denerwujące (delikatnie mówiąc).

Możesz to rozwiązać, zmieniając klasę bazową jako „klasę częściową”, a następnie kontynuować tworzenie klas wewnętrznych w swoich własnych plikach (pamiętaj tylko, że będą one musiały zadeklarować uzupełnienie klasy bazowej a następnie kontynuować z określoną klasą wewnętrzną dla tego pliku).

Coś jak...

Scenegraph.cs:

namespace MyLib
{
    public partial class Scenegraph
    {
        //Scenegraph specific implementations
    }
}

DependentClass.cs:

namespace MyLib
{
    public partial class Scenegraph
    {
        public class DependentClass
        {
            //DependentClass specific implementations
        }
    }
}

Myślę, że jest to tym bliższe do uzyskania czystej implementacji klas wewnętrznych, bez konieczności zaśmiecania wszystkiego w jednym ogromnym i niechlujnym pliku.

Marcelo Myara
źródło
1

Dzieje się tak, gdy jest to główna klasa przestrzeni nazw. Więc jedną z motywacji jest umieszczenie przestrzeni nazw w bibliotece, a problem zniknie, jeśli dodasz „Lib” do nazwy przestrzeni nazw ...

namespace SocketLib
{
    class Socket
    {
Paul Sumpner
źródło