Использование статических классов в качестве пространств имен


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

Смысл в том, что пространства имен могут быть скрыты с помощью ключевого слова «using». Но используя статические классы, необходимо указывать имена классов внешнего уровня, что эффективно сохраняет пространства имен.

Microsoft рекомендует против этого в руководящих принципах . Я лично думаю, что это влияет на читабельность. о чем ты думаешь?


1
Реализатору решать, нужны ли им пространства имен, присутствующие в реализации. Почему вы навязываете им использование пространств имен (и при этом поддельных имен)?
Крейг,

Может быть в целях группировки? Как будто есть много подклассов и путем группировки подклассов в статические классы. Что проясняет намерение?
user394128 10.11.11

Разве размещение подклассов в подпространстве имен также не прояснит намерение? Кроме того, это тип проблемы, решаемой с помощью документации.
Крейг,

Ответы:


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 ++, пространство имен является не только естественным выбором, но правильно и идиоматических выбор также .
Наваз

Я думаю, что OP намерен избежать скрытия пространства имен с помощью ключевого слова using. Он хочет заставить пользователей писать это каждый раз. Может быть, его пространство имен выглядит какvar s = new HideMe.MyPhoneNumberIs12345678.TheRealUsefulClass()
Gqqnbig

5

Все, что здесь происходит, определенно не является идиоматическим кодом C #.

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


Я согласен, выглядит странно.
Марко

4

Прежде всего, каждый класс должен находиться в пространстве имен, поэтому ваш пример будет выглядеть так:

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

Тем не менее, я не вижу никакой пользы от такого рода гимнастики кода, когда вы можете точно так же определить такое пространство имен:

namespace SomeNamespace.CategoryA { ... }

Если вы когда-нибудь задумываетесь о хранении некоторых статических методов на уровне высшего класса, это может иметь смысл, но все же это не то, что имеют в виду создатели C #, а может просто сделать его менее читаемым для других - я бы потратил много времени подумав, ПОЧЕМУ вы это сделали, и подумав, если мне не хватает какого-нибудь исходного файла, который предоставит объяснения


Так почему перечисления являются исключением из правила ?
sq33G

Это другой вопрос :) Но я бы все равно не определил статический класс только для того, чтобы определить в нем перечисления.
Змилойко

1

Пространства имен в Java, JavaScript и .NET связаны не полностью и позволяют хранить только классы, но другие вещи, такие как константы или глобальные методы, этого не делают.

Многие разработчики используют уловки «статические классы» или «статические методы», даже если некоторые люди не рекомендуют.


0

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

Суть этого в том, что при использовании обобщенных элементов для абстрагирования огромных участков стандартного кода иногда необходимо совместно использовать несколько общих параметров между связанными классами и интерфейсами. Обычный способ сделать это - переопределить общие параметры, ограничения и все в каждом интерфейсе и сигнатуре класса. Со временем это может привести к распространению параметров и ограничений, не говоря уже о необходимости постоянно квалифицировать связанные типы, перенаправляя параметры типа из одного типа в аргументы типа связанного типа.

Использование внешнего класса Generic и вложение связанных типов в него может сильно высушить код и упростить его абстракцию. Затем можно получить класс параметрического пространства имен с конкретной реализацией, которая предоставляет все конкретные детали.

Вот тривиальный пример:

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

    }
}

Преимущество написания типов таким способом заключается в дополнительной безопасности типов и меньшем приведении типов к абстрактным классам. Это эквивалент ArrayListпротив List<T>в большем масштабе.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.