Використання статичних класів як просторів імен


21

Я бачив інших розробників, які використовують статичні класи як простори імен

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

Для інстанції внутрішніх класів це буде виглядати наступним чином

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

Обґрунтування полягає в тому, що простори імен можна приховати, використовуючи ключове слово "використовуючи". Але за допомогою статичних класів слід вказати імена класів зовнішнього рівня, що ефективно зберігає простори імен.

Корпорація Майкрософт не радить цього в інструкціях . Я особисто думаю, що це впливає на читабельність. Які ваші думки?


1
Це залежить від того, чи потрібні їм простори імен, присутні в реалізації. Чому б ви змусили використовувати на них простори імен (і фальшиві простори імен)?
Крейдж

Може бути для цілей групування? Начебто є багато підкласів і шляхом групування підкласів до статичних класів. Це зробить зрозумілим наміри?
користувач394128

Якщо розміщення підкласів у просторі імен також не стане зрозумілим наміром? Також це тип проблеми, що вирішується документацією.
Крейдж

Відповіді:


16

Використання статичних класів як просторів імен не відповідає цілі створення просторів імен.

Ключова різниця тут:

Якщо ви визначаєте CategoryA, CategoryB<як простори імен, і коли програма використовує два простори імен:

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

Тут, якщо CategoryAабо CategoryBє статичний клас, а не простір імен, використання для програми майже таке ж, як описано вище.

Однак якщо ви визначаєте його як простір імен, і додаток використовує лише 1 простір імен (не включає CategoryB), у цьому випадку програма фактично може використовувати наступні

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

Але якби ви визначили CategoryAяк статичний клас, вищевикладене не визначено! Кожен змушений писати CategoryA.somethingщоразу.

Простори імен слід використовувати для уникнення конфліктів імен, тоді як ієрархія класів повинна використовуватися, коли групування класів має певне значення для системної моделі.


Крім того, якщо хтось хоче не використовувати простори імен, навіть використовуючи класи, може: using Item1 = CategoryA.Item1(я думаю, що це працює для вкладених класів, як це робиться для просторів імен)
Джордж Дакетт

1
У C ++ ADL не працює з класом; він працює з областю простору імен. Таким чином , в C ++, простір імен є не тільки природним вибором, але правильно і ідіоматичних вибір також .
Наваз

Я думаю, що ОП має намір уникати приховування простору імен за допомогою ключового слова. Він хоче змусити користувачів щоразу засвідчувати це. Можливо, його простір імен схожийvar s = new HideMe.MyPhoneNumberIs12345678.TheRealUsefulClass()
Gqqnbig

5

Що б тут не відбувалося, звичайно, це не ідіоматичний код C #.

Можливо, ці інші намагаються реалізувати шаблон модуля - це дозволить вам мати функції, дії тощо, а також класи в «просторі імен» (тобто статичний клас у цьому випадку)?


Я згоден, виглядає дивно.
marko

4

Перш за все, кожен клас повинен знаходитись у просторі імен, тож ваш приклад насправді більше нагадує:

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

З цього приводу я не бачу користі від подібного роду кодової гімнастики, коли можна просто так само визначити такий простір імен:

namespace SomeNamespace.CategoryA { ... }

Якщо ви, можливо, якийсь день думаєте про збереження якихось статичних методів на рівні вищого класу, це може мати сенс, але все-таки це не те, що мають на увазі творці C #, і це може просто зробити його менш читабельним для інших - я б витрачав багато часу думаючи, ЧОМУ ви це зробили, і думаєте, якщо я пропускаю якийсь вихідний файл, який би надав пояснення.


То чому ж перерахунки є виключенням із правила ?
sq33G

Це інше питання :) Але я все одно не визначив би статичний клас тільки для того, щоб визначити перерахунки в ньому.
zmilojko

1

Простори імен у Java, JavaScript та .NET, пов’язані із цим, не є повними, і дозволяють зберігати лише класи, але інші речі, такі як константи чи глобальні методи, не роблять.

Багато розробників використовують прийоми "статичних класів" або "статичних методів", навіть якщо деякі люди не рекомендують.


0

Я знаю, що це давнє запитання, але однією з дуже вагомих причин використовувати клас як простір імен (нестатичний у цьому) є те, що C # не підтримує визначення параметричних чи загальних просторів імен. Я написав повідомлення в блозі на цю саму тему тут: http://tyreejackson.com/generics-net-part5-generic-namespaces/ .

Суть її полягає в тому, що при використанні дженерики для абстрагування величезних фрагментів кодового шаблону іноді доводиться ділити декілька загальних параметрів між пов'язаними класами та інтерфейсами. Умовному способі зробити це було б перевизначити загальні параметри, обмеження та все в кожному інтерфейсі та підписі класу. З часом це може призвести до поширення параметрів і обмежень, не кажучи вже про необхідність постійно кваліфікувати відповідні типи, пересилаючи параметри типу від одного типу до аргументів типу відповідного типу.

Використання зовнішнього загального класу та вкладення відповідних типів усередині може різко ЗУБИТИ код та спростити його абстрагування. Потім можна отримати параметричний клас простору імен з конкретною реалізацією, яка забезпечує всі конкретні деталі.

Ось тривіальний приклад:

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

    }

}

Використовується так:

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

Споживайте такі похідні:

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

    }
}

Користь для написання типів таким чином додає безпеку типу та меншу кількість викидів у абстрактні класи. Його еквівалент ArrayListvs List<T>у більшому масштабі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.