Страницы

Поиск по вопросам

понедельник, 9 марта 2020 г.

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

#c_sharp #классы


Есть базовый абстрактный класс и 3 других класса, которые последовательно наследуют
реализацию предыдущего (BaseAbstract->A->B->C). При создании нового экземпляра одного
из этих трех классов, задействуется статическое поле (счетчик) в соответствующем классе
для подсчета суммарного количества экземпляров конкретного класса, вывод которого осуществляется
через статический метод.

abstract class BaseAbstract
{
    public BaseAbstract()
    {
        //инициализация полей абстрактного класса

        IncStaticField();   //вызов абстрактного метода в конструкторе абстрактного
класса
    }
    protected abstract void IncStaticField();
}

class A : BaseAbstract 
{
    private static int instNo = 0;  //стат. поле

    protected override void IncStaticField()
    {
        A.instNo = A.instNo + 1;   //тип указываю здесь лишь для "пояснения" собственных
намерений
    }

    public static string Info()     //вывод кол-ва созданных экземпляров конкретного
класса
    {
        return "Общее количество созданных экземпляров класса A: " + instNo;
    }
}

class B : A
{
    private static int instNo = 0;

    protected override void IncStaticField()
    {
        B.instNo = B.instNo + 1;
    }

    public static new string Info()     // замещение
    {
        return "Общее количество созданных экземпляров класса B: " + instNo;
    }
}

class C : B
{
    //аналогичная реализация, как в классе B
}


Данная реализация успешно подсчитывает кол-во созданных экземпляров для каждого класса
и выводит их значения через вызов метода Info() конкретного класса.

1) Насколько безопасным является вызов абстрактного (виртуального) метода в конструкторе,
благодаря которому и происходит увеличение статического поля конкретного класса?

2) Можно ли упростить реализацию метода IncStaticField(), дабы не приходилось объявлять
его в каждом классе лишь для указания класса статического поля которое следует увеличить?
    


Ответы

Ответ 1



Для начала пару слов про ваш код: public static new string Info() // замещение new в данном случае не требуется, т.к. CLR, вообще, и C#, в частности, не поддерживают наследование статических членов, соответственно никакой проблемы с одноименными статическими методами в классах наследниках не возникнет. 1) Насколько безопасным является вызов абстрактного (виртуального) метода в конструкторе, благодаря которому и происходит увеличение статического поля конкретного класса? Если абстрактный/виртуальный метод корректно реализован/переопределен с использованием override, то все будет нормально. Но проблема с вероятностью возникнет, если виртуальный метод не переопределен в классе и вызывает метод базового класса, который еще не был инстанцирован. Поэтому в целом такой подход не желателен, т.к. мы не можем заставить производный класс принудительно переопределить виртуальный метод. 2) Можно ли упростить реализацию метода IncStaticField(), дабы не приходилось объявлять его в каждом классе лишь для указания класса статического поля которое следует увеличить? Если метод используется исключительно для увеличения счетчика объектов, то почему бы не заменить его на обычный instNo++ в конструкторе? Таким образом определять метод в каждом классе не нужно. А вот более сложную логику, если она есть/нужна, уже можно выпилить в отдельный метод, и переопределять его по мере необходимости. Ну и напоследок полезная статья про статические члены классов и классы: Эрик Браун Семь советов по применению статических полей, методов и классов

Ответ 2



Как вариант, могу предложить завести в самом базовом классе одно статическое поле -- словарь (Dictionary) с ключом -- тип класса либо название типа класса, и значением int. И один метод (невиртуальный), который и будет всё делать. В метод передаём Type. Принцип работы метода такой: увеличить счётчик в словаре для переданного типа, затем по reflection взять для этого типа тип-родитель, увеличить счётчик в словаре для него итд. Ну и обеспечить вызов этого метода-счётчика из конструктора каждого порождённого класса. Плюсы -- никаких виртуальных вызовов в конструкторе. Минусы -- использование рефлексии, снижающее производительность.

Комментариев нет:

Отправить комментарий