Utilizzo di classi statiche come spazi dei nomi


21

Ho visto altri sviluppatori usare classi statiche come spazi dei nomi

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() { }
    }
}

Per creare un'istanza delle classi interne, sarà simile al seguente

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

La logica è che gli spazi dei nomi possono essere nascosti usando la parola chiave "using". Ma usando le classi statiche, è necessario specificare i nomi delle classi del livello esterno, che preservano efficacemente gli spazi dei nomi.

Microsoft sconsiglia questo nelle linee guida . Personalmente penso che abbia un impatto sulla leggibilità. Quali sono i tuoi pensieri?


1
Spetta all'implementatore se hanno bisogno degli spazi dei nomi presenti nell'implementazione. Perché dovresti forzare l'uso degli spazi dei nomi (e degli spazi dei nomi falsi)?
Craige,

Può essere a fini di raggruppamento? Come se ci fossero molte sottoclassi e raggruppando le sottoclassi in classi statiche. Ciò rende chiara l'intensione?
user394128,

Mettere le sottoclassi in uno spazio dei sotto-nomi non chiarirebbe anche l'intenzione? Inoltre, questo è il tipo di problema risolto dalla documentazione.
Craige,

Risposte:


16

L'uso di classi statiche come spazi dei nomi sfida lo scopo di avere spazi dei nomi.

La differenza chiave è qui:

Se si definisce CategoryA, CategoryB<come spazi dei nomi e quando l'applicazione utilizza due spazi dei nomi:

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

Qui se CategoryAo CategoryBè una classe statica anziché uno spazio dei nomi, l'utilizzo per l'applicazione è quasi lo stesso descritto sopra.

Tuttavia, se lo definisci come spazio dei nomi e l'applicazione utilizza solo 1 spazio dei nomi (non include CategoryB), in tal caso l'applicazione può effettivamente utilizzare

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

Ma se avessi definito CategoryAuna classe statica quanto sopra non è definito! Uno è costretto a scrivere CategoryA.somethingogni volta.

Gli spazi dei nomi dovrebbero essere utilizzati per evitare conflitti di denominazione, mentre la gerarchia di classi dovrebbe essere usata quando il raggruppamento di classi ha una certa rilevanza per il modello di sistema.


Inoltre, se qualcuno vuole non usare gli spazi dei nomi, anche usando le classi can: using Item1 = CategoryA.Item1(penso che funzioni per le classi nidificate come per gli spazi dei nomi)
George Duckett,

1
In C ++, ADL non funziona con l'ambito della classe; funziona con l'ambito dello spazio dei nomi. Quindi in C ++, lo spazio dei nomi non è solo una scelta naturale, ma anche una scelta corretta e idiomatica .
Nawaz,

Penso che sia intenzione di OP evitare di nascondere lo spazio dei nomi usando la parola chiave using. Vuole costringere gli utenti a scriverlo ogni volta. Forse il suo spazio dei nomi è simile avar s = new HideMe.MyPhoneNumberIs12345678.TheRealUsefulClass()
Gqqnbig

5

Qualunque cosa stia succedendo qui certamente non è un codice C # idiomatico.

Forse questi altri stanno cercando di implementare il modello del modulo - questo ti permetterebbe di avere funzioni, azioni e così via, nonché classi nello "spazio dei nomi" (cioè la classe statica in questo caso)?


Sono d'accordo, sembra strano.
marko,

4

Prima di tutto, ogni classe dovrebbe essere in uno spazio dei nomi, quindi il tuo esempio sarebbe in realtà più simile a:

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

Detto questo, non vedo alcun beneficio da questo tipo di ginnastica di codice quando puoi definire un tale spazio dei nomi:

namespace SomeNamespace.CategoryA { ... }

Se forse un giorno stai pensando di archiviare alcuni metodi statici a livello di classe superiore, questo potrebbe avere senso, ma non è ancora ciò che i creatori di C # hanno in mente, e potrebbe solo renderlo meno leggibile da altri - perderei molto tempo pensando PERCHÉ hai fatto questo, e pensando se mi manca qualche file sorgente che fornirebbe spiegazioni.


Quindi, perché gli enum sono l' eccezione alla regola ?
sq33G

Questa è una domanda diversa :) Ma non definirei ancora una classe statica solo per definire enumerazioni in essa.
zmilojko,

1

Gli spazi dei nomi in Java, JavaScript e .NET correlati non sono completi e consentono solo di memorizzare classi, ma altre cose come costanti o metodi globali non lo fanno.

Molti sviluppatori usano i trucchi "classi statiche" o "metodi statici", anche se alcune persone non lo consigliano.


0

So che questa è una vecchia domanda, ma un motivo molto valido per usare una classe come spazio dei nomi (non statico) è che C # non supporta la definizione di spazi dei nomi parametrici o generici. Ho scritto un post sul blog proprio su questo argomento qui: http://tyreejackson.com/generics-net-part5-generic-namespaces/ .

L'essenziale è che, quando si usano i generici per sottrarre enormi fasce di codice boilerplate, a volte è necessario condividere più parametri generici tra classi e interfacce correlate. Il modo convenzionale per farlo sarebbe ridefinire i parametri generici, i vincoli e tutto in ogni interfaccia e firma della classe. Nel tempo, ciò può portare a una proliferazione di parametri e vincoli per non parlare del fatto che è necessario qualificare costantemente i tipi correlati inoltrando i parametri di tipo da un tipo agli argomenti di tipo del tipo correlato.

L'uso di una classe generica esterna e l'annidamento dei tipi correlati all'interno possono DRY drasticamente il codice e semplificarne l'astrazione. Si può quindi derivare la classe dello spazio dei nomi parametrico con un'implementazione concreta che fornisce tutti i dettagli concreti.

Ecco un esempio banale:

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

    }

}

Usato così:

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

Consuma i derivati ​​in questo modo:

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

    }
}

Il vantaggio di scrivere i tipi in questo modo è l'aggiunta della sicurezza dei tipi e meno casting dei tipi nelle classi astratte. È l'equivalente di ArrayListvs List<T>su una scala più ampia.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.