Używanie klas statycznych jako przestrzeni nazw

21

Widziałem innych programistów używających klas statycznych jako przestrzeni nazw

public static class CategoryA
{
    public class Item1
    {
        public void DoSomething() { }
    }
    public class Item2
    {
        public void DoSomething() { }
    }
}

public static class CategoryB
{
    public class Item3
    {
        public void DoSomething() { }
    }
    public class Item4
    {
        public void DoSomething() { }
    }
}

Aby utworzyć instancję klas wewnętrznych, będzie to wyglądać następująco

CategoryA.Item1 item = new CategoryA.Item1();

Uzasadnieniem jest to, że przestrzenie nazw można ukryć za pomocą słowa kluczowego „using”. Ale używając klas statycznych, należy określić nazwy klas warstw zewnętrznych, co skutecznie zachowuje przestrzenie nazw.

Microsoft odradza to w wytycznych . Osobiście uważam, że wpływa to na czytelność. Jakie są Twoje myśli?

użytkownik394128
źródło
1
Od implementatora zależy, czy będą potrzebować przestrzeni nazw obecnych w implementacji. Dlaczego wymusiłbyś na nich użycie przestrzeni nazw (i fałszywych przestrzeni nazw)?
Craige
Może być do celów grupowania? Podobnie jak istnieje wiele podklas i grupowanie podklas do klas statycznych. To wyjaśnia intencję?
user394128,
Czy umieszczenie podklas w przestrzeni nazw nie wyjaśniłoby również intencji? Jest to także rodzaj problemu rozwiązanego w dokumentacji.
Craige

Odpowiedzi:

16

Używanie klas statycznych jako przestrzeni nazw jest sprzeczne z celem posiadania przestrzeni nazw.

Kluczowa różnica jest tutaj:

Jeśli zdefiniujesz CategoryA, CategoryB<jako przestrzenie nazw i gdy aplikacja używa dwóch przestrzeni nazw:

CategoryA::Item1 item = new CategoryA::Item1(); 
CategoryB::Item1 item = new CategoryB::Item1();

W przypadku, gdy CategoryAlub CategoryBjest klasą statyczną, a nie przestrzenią nazw, użycie aplikacji jest prawie takie samo, jak opisano powyżej.

Jeśli jednak zdefiniujesz go jako przestrzeń nazw, a aplikacja używa tylko 1 przestrzeni nazw (nie obejmuje CategoryB), w takim przypadku aplikacja może faktycznie użyć następujących

using namespace CategoryA; 
Item1 item = new Item1(); 

Ale gdybyś zdefiniował CategoryAjako klasę statyczną, powyższe nie jest zdefiniowane! Za CategoryA.somethingkażdym razem trzeba pisać .

Przestrzeni nazw należy używać, aby uniknąć konfliktów nazw, natomiast hierarchii klas należy używać, gdy grupowanie klas ma pewne znaczenie dla modelu systemu.

Dipan Mehta
źródło
Ponadto, jeśli ktoś nie chce używać przestrzeni nazw, nawet używając klas, może: using Item1 = CategoryA.Item1(myślę, że działa to w przypadku klas zagnieżdżonych, tak jak w przypadku przestrzeni nazw)
George Duckett
1
W C ++ ADL nie działa z zakresem klasy; działa z zakresem przestrzeni nazw. Tak więc w C ++ przestrzeń nazw to nie tylko naturalny wybór, ale także wybór poprawny i idiomatyczny .
Nawaz
Myślę, że intencją OP jest unikanie ukrywania przestrzeni nazw za pomocą słowa kluczowego using. Chce zmusić użytkowników do napisania tego za każdym razem. Może jego przestrzeń nazw jest jakvar s = new HideMe.MyPhoneNumberIs12345678.TheRealUsefulClass()
Gqqnbig
5

Cokolwiek się tu dzieje, z pewnością nie jest idiomatycznym kodem C #.

Może ci inni próbują zaimplementować wzorzec modułu - pozwoliłoby to mieć funkcje, akcje itd., A także klasy w „przestrzeni nazw” (tj. W tym przypadku klasę statyczną)?

FinnNk
źródło
Zgadzam się, wygląda dziwnie.
marko,
4

Przede wszystkim każda klasa powinna znajdować się w przestrzeni nazw, więc twój przykład byłby bardziej podobny do:

SomeNamespace.CategoryA.Item1 item = new SomeNamespace.CategoryA.Item1();

To powiedziawszy, nie widzę korzyści z tego rodzaju gimnastyki w kodzie, gdy równie dobrze można zdefiniować taką przestrzeń nazw:

namespace SomeNamespace.CategoryA { ... }

Jeśli zastanawiasz się kiedyś nad przechowywaniem niektórych metod statycznych na poziomie wyższej klasy, może to mieć sens, ale twórcy C # nie mają tego na myśli i mogą uczynić go mniej czytelnym dla innych - zmarnowałbym dużo czasu myślenie DLACZEGO to zrobiłeś i zastanawianie się, czy brakuje mi jakiegoś pliku źródłowego zawierającego wyjaśnienia.

zmilojko
źródło
Dlaczego więc enum jest wyjątkiem od reguły ?
sq33G 10.11.11
To inne pytanie :) Ale nadal nie zdefiniowałbym klasy statycznej tylko po to, aby zdefiniować w niej wyliczenia.
zmilojko,
1

Przestrzenie nazw w języku Java, JavaScript i .NET nie są kompletne i pozwalają tylko na przechowywanie klas, ale inne rzeczy, takie jak stałe lub metody globalne, nie.

Wielu programistów stosuje sztuczki „klas statycznych” lub „metod statycznych”, nawet jeśli niektórzy ludzie tego nie zalecają.

umlcat
źródło
0

Wiem, że jest to stare pytanie, ale jednym z bardzo uzasadnionych powodów używania klasy jako przestrzeni nazw (w tym niestatycznej) jest to, że C # nie obsługuje definicji parametrycznych lub ogólnych przestrzeni nazw. Napisałem post na blogu na ten właśnie temat tutaj: http://tyreejackson.com/generics-net-part5-generic-namespaces/ .

Jego sedno polega na tym, że gdy używa się generics do abstrakcyjnego pokrywania ogromnego obszaru kodu, czasem konieczne jest współdzielenie wielu ogólnych parametrów między pokrewnymi klasami i interfejsami. Konwencjonalnym sposobem na to byłoby ponowne zdefiniowanie ogólnych parametrów, ograniczeń i wszystkich w każdym interfejsie i sygnaturze klasy. Z czasem może to prowadzić do mnożenia parametrów i ograniczeń, nie mówiąc już o konieczności ciągłego kwalifikowania powiązanych typów poprzez przekazywanie parametrów typu z jednego typu do argumentów typu powiązanego typu.

Użycie zewnętrznej klasy Generic i zagnieżdżenie powiązanych typów może radykalnie WYSUSIĆ kod i uprościć jego abstrakcję. Następnie można wyprowadzić parametryczną klasę przestrzeni nazw z konkretną implementacją, która dostarcza wszystkie konkretne szczegóły.

Oto prosty przykład:

public  class   Entity
                <
                    TEntity, 
                    TDataObject, 
                    TDataObjectList, 
                    TIBusiness, 
                    TIDataAccess, 
                    TIdKey
                >
        where   TEntity         : Entity<TEntity, TDataObject, TDataObjectList, TIBusiness, TIDataAccess, TIdKey>, subclassed
        where   TDataObject     : Entity<TEntity, TDataObject, TDataObjectList, TIBusiness, TIDataAccess, TIdKey>.BaseDataObject, subclassed
        where   TDataObjectList : Entity<TEntity, TDataObject, TDataObjectList, TIBusiness, TIDataAccess, TIdKey>.BaseDataObjectList, subclassed
        where   TIBusiness      : Entity<TEntity, TDataObject, TDataObjectList, TIBusiness, TIDataAccess, TIdKey>.IBaseBusiness
        where   TIDataAccess    : Entity<TEntity, TDataObject, TDataObjectList, TIBusiness, TIDataAccess, TIdKey>.IBaseDataAccess
{

    public class    BaseDataObject
    {
        public TIdKey Id { get; set; }
    }

    public class BaseDataObjectList : Collection<TDataObject> {}

    public interface IBaseBusiness
    {

        TDataObject     LoadById(TIdKey id);
        TDataObjectList LoadAll();
        void            Save(TDataObject item);
        void            Save(TDataObjectList items);
        void            DeleteById(TIdKey id);
        bool            Validate(TDataObject item);
        bool            Validate(TDataObjectList items);

    }

    public interface IBaseDataAccess
    {

        TDataObject     LoadById(TIdKey id);
        TDataObjectList LoadAll();
        void            Save(TDataObject item);
        void            Save(TDataObjectList items);
        void            DeleteById(TIdKey id);

    }

}

Używany w ten sposób:

public  class   User 
:
                Entity
                <
                    User, 
                    User.DataObject, 
                    User.DataObjectList, 
                    User.IBusiness, 
                    User.IDataAccess, 
                    Guid
                >
{
    public class DataObject : BaseDataObject
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

    public class DataObjectList : BaseDataObjectList {}

    public interface IBusiness : IBaseBusiness
    {
        void DeactivateUserById(Guid id);
    }

    public interface IDataAcccess : IBaseDataAccess {}
}

Korzystaj z pochodnych w następujący sposób:

public class EntityConsumer
{
    private User.IBusiness       userBusiness;
    private Permission.IBusiness permissionBusiness;

    public EntityConsumer(User.IBusiness userBusiness, Permission.IBusiness permissionBusiness) { /* assign dependencies */ }

    public void ConsumeEntities()
    {
        var users       = new User.DataObjectList();
        var permissions = this.permissionBusiness.LoadAll();

        users.Add
        (new User.DataObject()
        {
            // Assign property values
        });

        this.userBusiness.Save(users);

    }
}

Korzyścią z pisania typów w ten sposób jest zwiększenie bezpieczeństwa typów i mniej rzucania typów w klasach abstrakcyjnych. Jest to odpowiednik ArrayListvs List<T>na większą skalę.

Tyree Jackson
źródło