Usando classes estáticas como namespaces


21

Eu já vi outros desenvolvedores usando classes estáticas como namespaces

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

Para instanciar as classes internas, será semelhante ao seguinte

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

A lógica é que os espaços para nome podem ser ocultados usando a palavra-chave "using". Mas, usando as classes estáticas, os nomes das classes da camada externa precisam ser especificados, o que preserva efetivamente os espaços para nome.

A Microsoft desaconselha isso nas diretrizes . Pessoalmente, acho que isso afeta a legibilidade. Quais são seus pensamentos?


1
Cabe ao implementador se eles precisam dos espaços para nome presentes na implementação. Por que você forçaria o uso dos namespaces (e namespaces falsos) neles?
Craige

Pode ser para fins de agrupamento? Como existem muitas subclasses e agrupando as subclasses em classes estáticas. Isso deixa clara a intenção?
user394128

Colocar as subclasses em um subdomínio também não deixaria clara a intenção? Além disso, esse é o tipo de problema resolvido pela documentação.
Craige

Respostas:


16

O uso de classes estáticas como espaços de nomes desafia o propósito de ter espaços de nomes.

A principal diferença está aqui:

Se você definir CategoryA, CategoryB<como espaços para nome, e quando o aplicativo usar dois espaços para nome:

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

Aqui, se o CategoryAou CategoryBé uma classe estática em vez de um espaço para nome, o uso do aplicativo é quase o mesmo descrito acima.

No entanto, se você o definir como um espaço para nome e o aplicativo usar apenas 1 espaço para nome (não inclui CategoryB), nesse caso, o aplicativo poderá realmente usar os seguintes

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

Mas se você definisse CategoryAcomo uma classe estática, o acima é indefinido! Um é forçado a escrever CategoryA.somethingtodas as vezes.

Os espaços para nome devem ser usados ​​para evitar conflitos de nomenclatura, enquanto a hierarquia de classes deve ser usada quando o agrupamento de classes tiver alguma relevância para o modelo do sistema.


Além disso, se alguém quiser não usar namespaces, mesmo usando classes, o can: using Item1 = CategoryA.Item1(acho que funciona para classes aninhadas, assim como para namespaces) #
687 George Duckett

1
No C ++, o ADL não funciona com o escopo da classe; funciona com o escopo do espaço para nome. Portanto, em C ++, o espaço para nome não é apenas uma escolha natural, mas também uma escolha correta e idiomática .
Nawaz

Acho que é intenção do OP evitar ocultar o namespace usando a palavra-chave Ele quer forçar os usuários a escrevê-lo sempre. Talvez o seu espaço para nome seja comovar s = new HideMe.MyPhoneNumberIs12345678.TheRealUsefulClass()
Gqqnbig 15/03

5

O que está acontecendo aqui certamente não é um código C # idiomático.

Talvez esses outros estejam tentando implementar o padrão do módulo - isso permitiria que você tivesse funções, ações e assim por diante, bem como classes no 'namespace' (por exemplo, classe estática neste caso)?


Eu concordo, parece estranho.
Mar11:

4

Antes de tudo, toda classe deve estar em um espaço para nome, portanto, seu exemplo seria mais como:

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

Dito isso, não vejo nenhum benefício desse tipo de ginástica de código quando você pode definir um espaço para nome:

namespace SomeNamespace.CategoryA { ... }

Se você está pensando um dia em armazenar alguns métodos estáticos no nível de classe alta, isso pode fazer sentido, mas ainda não é o que os criadores de C # têm em mente e pode torná-lo menos legível para os outros - eu perderia muito tempo pensando POR QUE você fez isso e pensando se estou faltando algum arquivo de origem que forneça explicações.


Então, por que as enums são uma exceção à regra ?
sq33G

Essa é uma pergunta diferente :) Mas eu ainda não definiria uma classe estática apenas para definir enumerações nela.
zmilojko

1

Os espaços para nome relacionados a Java, JavaScript e .NET não estão completos e apenas permitem armazenar classes, mas outras coisas, como constantes ou métodos globais, não.

Muitos desenvolvedores usam os truques "classes estáticas" ou "métodos estáticos", mesmo que algumas pessoas não o recomendem.


0

Eu sei que essa é uma pergunta antiga, mas um motivo muito válido para usar uma classe como um espaço para nome (um não estático) é que o C # não suporta a definição de espaços para nome paramétricos ou genéricos. Eu escrevi uma postagem de blog sobre esse mesmo tópico aqui: http://tyreejackson.com/generics-net-part5-generic-namespaces/ .

A essência disso é que, ao usar genéricos para abstrair grandes faixas de código padrão, às vezes é necessário compartilhar vários parâmetros genéricos entre classes e interfaces relacionadas. A maneira convencional de fazer isso seria redefinir os parâmetros genéricos, restrições e tudo em cada interface e assinatura de classe. Com o tempo, isso pode levar a uma proliferação de parâmetros e restrições, sem mencionar constantemente a necessidade de qualificar os tipos relacionados, encaminhando os parâmetros de tipo de um tipo para os argumentos de tipo do tipo relacionado.

Usar uma classe Genérica externa e aninhar os tipos relacionados pode secar drasticamente o código e simplificar sua abstração. Pode-se derivar a classe de espaço para nome paramétrico com uma implementação concreta que fornece todos os detalhes concretos.

Aqui está um exemplo 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);

    }

}

Usado assim:

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 os derivados como este:

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

    }
}

O benefício de escrever os tipos dessa maneira é aumentar a segurança de tipos e diminuir a conversão de tipos nas classes abstratas. É o equivalente de ArrayListvs List<T>em uma escala maior.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.