Usar clases estáticas como espacios de nombres

21

He visto a otros desarrolladores usando clases estáticas como espacios de nombres

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 las clases internas, se verá así:

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

La razón es que los espacios de nombres se pueden ocultar utilizando la palabra clave "using". Pero al usar las clases estáticas, se deben especificar los nombres de clase de la capa externa, lo que preserva efectivamente los espacios de nombres.

Microsoft desaconseja eso en las pautas . Personalmente, creo que afecta la legibilidad. ¿Cuáles son tus pensamientos?

usuario394128
fuente
1
Depende del implementador si necesitan los espacios de nombres presentes en la implementación. ¿Por qué forzarías el uso de los espacios de nombres (y los espacios de nombres falsos) en ellos?
Craige
Puede ser para fines de agrupación? Al igual que hay muchas subclases y agrupando las subclases a clases estáticas. ¿Que aclara la intención?
user394128
¿Poner las subclases en un subespacio de nombres tampoco aclararía la intención? Además, este es el tipo de problema resuelto por la documentación.
Craige

Respuestas:

16

El uso de clases estáticas como espacios de nombres desafía el propósito de tener espacios de nombres.

La diferencia clave está aquí:

Si define CategoryA, CategoryB<como espacios de nombres, y cuando la aplicación usa dos espacios de nombres:

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

Aquí, si CategoryAo CategoryBes una clase estática en lugar de un espacio de nombres, el uso de la aplicación es casi el mismo que el descrito anteriormente.

Sin embargo, si lo define como un espacio de nombres y la aplicación usa solo 1 espacio de nombres (no incluye CategoryB), en ese caso, la aplicación puede usar los siguientes

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

¡Pero si definiera CategoryAcomo una clase estática, lo anterior no está definido! Uno se ve obligado a escribir CategoryA.somethingcada vez.

Los espacios de nombres deben usarse para evitar conflictos de nombres, mientras que la jerarquía de clases debe usarse cuando la agrupación de clases tiene cierta relevancia para el modelo del sistema.

Dipan Mehta
fuente
Además, si alguien quiere no usar espacios de nombres, incluso usando clases, puede: using Item1 = CategoryA.Item1(creo que funciona para clases anidadas como lo hace para espacios de nombres)
George Duckett
1
En C ++, ADL no funciona con alcance de clase; Funciona con el alcance del espacio de nombres. Entonces, en C ++, el espacio de nombres no es solo una elección natural, sino también una elección correcta e idiomática .
Nawaz
Creo que la intención de OP es evitar ocultar el espacio de nombres mediante el uso de la palabra clave. Quiere obligar a los usuarios a escribirlo cada vez. Tal vez su espacio de nombres es comovar s = new HideMe.MyPhoneNumberIs12345678.TheRealUsefulClass()
Gqqnbig
5

Lo que está sucediendo aquí ciertamente no es un código idiomático de C #.

¿Quizás estos otros están tratando de implementar el patrón del módulo?

FinnNk
fuente
Estoy de acuerdo, se ve raro.
marko
4

En primer lugar, cada clase debe estar en un espacio de nombres, por lo que su ejemplo en realidad sería más como:

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

Dicho esto, no veo ningún beneficio de este tipo de gimnasia de código cuando puedes definir un espacio de nombres así:

namespace SomeNamespace.CategoryA { ... }

Si tal vez algún día está pensando en almacenar algunos métodos estáticos en el nivel superior de la clase, esto podría tener sentido, pero aún así no es lo que los creadores de C # tienen en mente, y podría hacer que sea menos legible para los demás; perdería mucho tiempo pensando POR QUÉ hiciste esto, y pensando si me falta algún archivo fuente que proporcione explicaciones.

zmilojko
fuente
Entonces, ¿por qué las enumeraciones son la excepción a la regla ?
sq33G
Esa es una pregunta diferente :) Pero todavía no definiría una clase estática solo para definir enumeraciones en ella.
zmilojko
1

Los espacios de nombres relacionados con Java, JavaScript y .NET no están completos y solo permiten almacenar clases, pero otras cosas como constantes o métodos globales no lo hacen.

Muchos desarrolladores usan los trucos de "clases estáticas" o "métodos estáticos", incluso si algunas personas no lo recomiendan.

umlcat
fuente
0

Sé que esta es una pregunta antigua, pero una razón muy válida para usar una clase como espacio de nombres (no estática) es que C # no admite la definición de espacios de nombres paramétricos o genéricos. He escrito una publicación de blog sobre este mismo tema. aquí: http://tyreejackson.com/generics-net-part5-generic-namespaces/ .

Lo esencial es que, cuando se usan genéricos para abstraer grandes extensiones de código repetitivo, a veces es necesario compartir múltiples parámetros genéricos entre clases e interfaces relacionadas. La forma convencional de hacer esto sería redefinir los parámetros genéricos, las restricciones y todo en cada interfaz y firma de clase. Con el tiempo, esto puede conducir a una proliferación de parámetros y restricciones, sin mencionar que constantemente tiene que calificar los tipos relacionados al reenviar los parámetros de un tipo a los argumentos de tipo del tipo relacionado.

El uso de una clase genérica externa y el anidamiento de los tipos relacionados dentro pueden DRYAR dramáticamente el código y simplificar su abstracción. Luego se puede derivar la clase de espacio de nombres paramétrico con una implementación concreta que proporciona todos los detalles concretos.

Aquí hay un ejemplo 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 así:

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

Consume los derivados así:

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

    }
}

El beneficio de escribir los tipos de esta manera es la seguridad de los tipos añadidos y menos conversión de tipos en las clases abstractas. Es el equivalente de ArrayListvs List<T>en una escala mayor.

Tyree Jackson
fuente