Utilisation de classes statiques comme espaces de noms

21

J'ai vu d'autres développeurs utiliser des classes statiques comme espaces de noms

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

Pour instancier les classes internes, cela ressemblera à ceci:

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

La raison en est que les espaces de noms peuvent être masqués à l'aide du mot clé "using". Mais en utilisant les classes statiques, les noms des classes de la couche externe doivent être spécifiés, ce qui préserve efficacement les espaces de noms.

Microsoft déconseille cela dans les directives . Je pense personnellement que cela a un impact sur la lisibilité. Quelles sont vos pensées?

user394128
la source
1
C'est à l'implémenteur de déterminer s'il a besoin des espaces de noms présents dans l'implémentation. Pourquoi voudriez-vous forcer l'utilisation des espaces de noms (et de faux espaces de noms à cela) sur eux?
Craige
Peut être à des fins de regroupement? Comme il y a beaucoup de sous-classes et en regroupant les sous-classes en classes statiques. Cela rend l'intension claire?
user394128
Le fait de placer les sous-classes dans un sous-espace de noms ne rendrait-il pas également l'intention claire? C'est également le type de problème résolu par la documentation.
Craige

Réponses:

16

L'utilisation de classes statiques comme espaces de noms défie le but d'avoir des espaces de noms.

La principale différence est ici:

Si vous définissez CategoryA, en CategoryB<tant qu'espaces de noms, et lorsque l'application utilise deux espaces de noms:

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

Ici, si CategoryAou CategoryBest une classe statique plutôt qu'un espace de noms, l'utilisation de l'application est presque la même que celle décrite ci-dessus.

Cependant, si vous le définissez comme un espace de noms et que l'application n'utilise qu'un seul espace de noms (ne comprend pas CategoryB), dans ce cas, l'application peut réellement utiliser les éléments suivants

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

Mais si vous aviez défini CategoryAune classe statique, ce qui précède n'est pas défini! On est obligé d'écrire à CategoryA.somethingchaque fois.

Les espaces de noms doivent être utilisés pour éviter les conflits de noms, tandis que la hiérarchie des classes doit être utilisée lorsque le regroupement de classes a une certaine pertinence pour le modèle de système.

Dipan Mehta
la source
De plus, si quelqu'un veut ne pas utiliser d'espaces de noms, même en utilisant des classes, le bidon: using Item1 = CategoryA.Item1(je pense que cela fonctionne pour les classes imbriquées comme pour les espaces de noms)
George Duckett
1
En C ++, ADL ne fonctionne pas avec la portée de classe; cela fonctionne avec la portée de l'espace de noms. Ainsi, en C ++, l'espace de noms n'est pas seulement un choix naturel, mais aussi un choix correct et idiomatique .
Nawaz
Je pense que l'intention de OP est d'éviter de cacher l'espace de noms par le mot-clé using. Il veut forcer les utilisateurs à l'écrire à chaque fois. Peut-être que son espace de noms est commevar s = new HideMe.MyPhoneNumberIs12345678.TheRealUsefulClass()
Gqqnbig
5

Ce qui se passe ici n'est certainement pas un code C # idiomatique.

Peut-être que ces autres essaient d'implémenter le modèle de module - cela vous permettrait d'avoir des fonctions, des actions, etc. ainsi que des classes dans «l'espace de noms» (c'est-à-dire une classe statique dans ce cas)?

FinnNk
la source
Je suis d'accord, ça a l'air bizarre.
marko
4

Tout d'abord, chaque classe devrait être dans un espace de noms, donc votre exemple serait en fait plus comme:

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

Cela dit, je ne vois aucun avantage à ce type de gymnastique de code lorsque vous pouvez tout aussi bien définir un tel espace de noms:

namespace SomeNamespace.CategoryA { ... }

Si vous pensez peut-être un jour à stocker des méthodes statiques au niveau de la classe supérieure, cela pourrait avoir du sens, mais ce n'est toujours pas ce que les créateurs de C # ont à l'esprit, et pourrait simplement le rendre moins lisible pour les autres - je perdrais beaucoup de temps penser POURQUOI vous avez fait cela, et penser si je manque un fichier source qui fournirait des explications.

zmilojko
la source
Alors pourquoi les énumérations sont-elles l' exception à la règle ?
sq33G
C'est une question différente :) Mais je ne définirais toujours pas une classe statique juste pour y définir des énumérations.
zmilojko
1

Les espaces de noms en Java, JavaScript et .NET ne sont pas complets et ne permettent que de stocker des classes, mais pas d'autres choses comme les constantes ou les méthodes globales.

De nombreux développeurs utilisent les astuces "classes statiques" ou "méthodes statiques", même si certaines personnes ne le recommandent pas.

umlcat
la source
0

Je sais que c'est une vieille question, mais une raison très valable d'utiliser une classe comme espace de noms (un non statique à cela) est que C # ne prend pas en charge la définition des espaces de noms paramétriques ou génériques. J'ai rédigé un article de blog sur ce sujet ici: http://tyreejackson.com/generics-net-part5-generic-namespaces/ .

L'essentiel est que, lors de l'utilisation de génériques pour résumer d'énormes portions de code passe-partout, il est parfois nécessaire de partager plusieurs paramètres génériques entre des classes et des interfaces liées. La manière conventionnelle de le faire serait de redéfinir les paramètres génériques, les contraintes et tout cela dans chaque interface et signature de classe. Au fil du temps, cela peut entraîner une prolifération de paramètres et de contraintes, sans parler de devoir constamment qualifier les types associés en transmettant les paramètres de type d'un type aux arguments de type du type associé.

L'utilisation d'une classe générique externe et l'imbrication des types associés au sein peuvent considérablement assécher le code et simplifier son abstraction. On peut ensuite dériver la classe d'espace de noms paramétrique avec une implémentation concrète qui fournit tous les détails concrets.

Voici un exemple trivial:

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

    }

}

Utilisé comme ceci:

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

Consommez les dérivés comme ceci:

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

    }
}

L'avantage d'écrire les types de cette manière est une sécurité de type accrue et moins de transtypage de types dans les classes abstraites. C'est l'équivalent de ArrayListvs List<T>à plus grande échelle.

Tyree Jackson
la source