Замещение исходного кода
(C#)
Замещающим конфигурационным элементом является класс. Для упрощения понимания можно использовать понятие «производный» вместо «замещающий», «базовый» вместо «замещаемый». У алгоритма замещения класса есть свои особенности, которые рассмотрены далее.
Создание замещающего класса
Для создания замещающего исходного кода следуйте шагам:
- Откройте раздел «Конфигурация».
- Выберите пакет, в который хотите добавить замещающую схему.
- Откройте свойства пакета. В блоке «Зависит от пакетов» проверьте наличие пакета, элемент которого хотите заместить. Если данного пакета нет, добавьте зависимость от нужного пакета.
- На панели инструментов рабочей области раздела «Конфигурация» нажмите «Добавить» и выберите «Исходный код».
Рисунок 1 — Конфигурационный элемент «Исходный код»

- В дизайнере исходного кода создайте класс, который является наследником замещаемого (базового).
- Отметьте замещающий (производный) класс атрибутом [Override].
- Реализуйте необходимую функциональность.
Переопределению подлежат свойства и методы базового класса, отмеченные ключевым словом virtual. Также можно реализовать абстрактные методы базового класса. В производном классе для свойств и методов добавьте ключевое слово override.
- Сохраните и опубликуйте класс.
Механизм замещения
Созданием экземпляра замещающего (производного) класса занимается фабрика объектов замещающих классов. При первом обращении система анализирует конфигурационную сборку, находит классы, помеченные Override, и строит соответствия между базовыми и производными типами. Если в цепочке наследования несколько уровней, итоговой заменой становится последний наследник, а промежуточные типы отдельно не используются как результат замещения.
Атрибут Override является наследником базового типа System.Attribute и находится в пространстве имен BPMSoft.Core.Factories. Подробнее с пространством имен BPMSoft.Core.Factories можно ознакомиться в Библиотеке .NET классов. Данный атрибут используется для построения дерева зависимостей замещающих (производных) и замещаемых (базовых) типов фабрики. Атрибут применяется только к классам.
Рассмотрим шаблон применения атрибута:
[Override]
/// <summary>
/// SubstituteClass -- Замещающий (производный) класс
/// SubstitutableClass -- Замещаемый (базовый) класс
/// </summary>
public class SubstituteClass : SubstitutableClass
{
// Реализация логики замещения класса
}
Для создания объекта используется статический класс ClassFactory. Данный класс реализует фабрику создания замещающих (производных) объектов. Фабрика использует open-source фреймворк внедрения зависимостей Ninject. Более подробно со статическим классом ClassFactory можно ознакомиться в библиотеке .NET классов.
В момент инициализации фабрики она собирает информацию обо всех замещаемых (базовых) типах. Инициализация происходит в момент первого обращения к ней, то есть при первой попытке получить экземпляр замещающего (производного) типа.
Алгоритм работы фабрики:
- Поиск замещающих (производных) типов.
Фабрика анализирует конфигурационную сборку. Помеченные атрибутом Override классы интерпретируются как замещающие (производные), а родители этих классов — как замещаемые (базовые). - Формирование дерева зависимостей.
Фабрика формирует список пар значений: замещаемый (базовый) тип → замещающий (производный) тип.
- Замещение исходного класса.
Исходный тип замещает последний в иерархии наследник. - На основе дерева зависимостей выполняется привязка типов замещения. Для этого используется фреймворк Ninject.
Рассмотрим пример получения экземпляра типа (класса): есть три класса:
- Класс A — базовый класс;
- Класс B — наследник класса A;
- Класс C — наследник класса B.
По иерархии классов будет простроено дерево зависимостей:
ClassA → ClassC
ClassB → ClassC
При построении дерева зависимостей не будут учтены промежуточные классы:
ClassA → ClassB → ClassC
ClassC в данном случае будет замещающим классом для ClassA и ClassB, так как является последним в иерархии. То есть при запросе экземпляра типа ClassA или ClassB фабрика вернет ClassC.
Получение экземпляра замещаемого типа
Для получения экземпляра замещаемого типа необходимо использовать метод Get<T> класса ClassFactory.
var substituteObject = ClassFactory.Get<SubstitutableClass>();
В результате будет создан экземпляр класса SubstituteClass. Подробнее ознакомиться с параметризированным методом Get<T> можно в Библиотеке .NET классов.
Метод Get<T> может принимать параметры конструктора в виде массива объектов ConstructorArgument. Каждый элемент такого массива соответствует одному аргументу конструктора создаваемого класса. Это позволяет фабрике создавать экземпляры замещающих классов с параметризованными конструкторами. При этом все остальные зависимости, необходимые для создания и работы объекта, фабрика определяет и инициализирует автоматически.
Конструкторы и создание экземпляров
Рекомендуется, чтобы сигнатуры конструкторов замещающего класса соответствовали сигнатурам конструкторов замещаемого класса. Если логика замещающего класса требует использования конструктора с пользовательской сигнатурой, при реализации следует учитывать один из следующих сценариев.
Варианты реализации конструкторов:
- Замещаемый класс использует только конструктор по умолчанию. В этом случае в замещающем классе допускается определение своего конструктора без каких-либо ограничений. При этом соблюдается стандартный порядок вызовов конструкторов родительского (замещаемого | базового) и дочернего (замещающего | производного) классов. В этом случае необходимо помнить, что при создании замещаемого класса через фабрику ей необходимо передавать параметры для корректной инициализации свойств замещающего класса;
- Замещаемый класс содержит параметризованный конструктор. В этом случае в замещающем классе также должен быть реализован конструктор. Конструктор замещающего класса должен явно вызывать параметризованный конструктор родительского (замещаемого | базового) класса и передавать ему параметры, необходимые для корректной инициализации родительских свойств. При этом конструктор замещающего класса может инициализировать собственные свойства либо оставаться пустым.
Несоблюдение правил приводит к ошибке во время выполнения. Корректность инициализации свойств замещающего (производного) и замещаемого (базового) классов в этом случае остается зоной ответственности разработчика.