Пример работы с замещающими классами

Замещение класса выполняется на уровне типа, для этого создается производный класс, который помечается атрибутом [Override]. При запросе экземпляра базового типа через ClassFactory.Get<T>() система возвращает экземпляр последнего замещающего класса в цепочке наследования.

1. Замещающий класс без параметров конструктора

Рассмотрим пример инициализации замещающего класса с конструктором по умолчанию и параметризированным конструктором. При этом у замещаемого класса только один конструктор по умолчанию.

Базовый класс BpmReplacedClass
/// <summary>
/// Замещаемый (базовый) класс.
/// </summary>
public class BpmReplacedClass 
{
	/// <summary>
	/// Свойство класса.
	/// </summary>
	public int OriginalValue { get; private set; }
	
	/// <summary>
	/// Конструктор по умолчанию.
	/// </summary>
	public BpmReplacedClass() {
		// Инициализация поля класса.
		OriginalValue =  5;
	}
	
	/// <summary>
	/// Метод, который может быть переопределен в замещающем классе. 
	/// </summary>
	public virtual int GetMultipliedValue(){
		return OriginalValue * 5;
	}
}
Производный класс BpmReplacementClass
/// <summary>
/// Замещающий (производный) класс.
/// </summary>

// Атрибут Override указывается для построения дерева зависимостей замещающих и замещаемых типов.
// Атрибут указывает на то, что данный класс являются замещающим.
[BPMSoft.Core.Factories.Override]
public class BpmReplacementClass : BpmReplacedClass
{
	/// <summary>
	/// Свойство класса BpmReplacementClass.
	/// </summary>
	public int AdditionalValue { get; private set; }
	/// <summary>
	/// Конструктор по умолчанию, который инициализирует свойство AdditionalValue значением 15.
	/// </summary>
	public BpmReplacementClass()
	{
		AdditionalValue = 15;
	}
	/// <summary>
	/// Конструктор с параметром, который инициализирует свойство AdditionalValue значением, переданным в качестве параметра
	/// </summary>
	public BpmReplacementClass(int additionalValue)
	{
		AdditionalValue = additionalValue;
	}
	/// <summary>
	/// Замещение родительского метода. Метод возвращает AdditionalValue, умноженное на 3.
	/// </summary>
	public override int GetMultipliedValue()
	{
		return AdditionalValue * 3;
	}
}

Для получения экземпляра замещающего класса воспользуемся фабрикой классов:

var replacementClass = ClassFactory.Get<BpmReplacedClass>();

Переменная OriginalValue содержит значение 5, а переменная AdditionalValue содержит значение 15. Обе переменные были инициализированы конструктором по умолчанию своих классов. Конструктор по умолчанию запускается в неявном виде. Метод GetMultipliedValue вернет значение 45.

Для инициализации замещающего класса с параметризированным конструктором воспользуемся следующей конструкцией:

var replacementClassWithParameters = ClassFactory.Get<BpmReplacedClass>(new ConstructorArgument("additionalValue", 20));

Конструктор класса ConstructorArgument принимает два параметра: название параметра конструктора производного класса; значение этого параметра.

Переменная OriginalValue в данном случае будет содержать значение 5, а переменная AdditionalValue содержит 20. Метод GetMultipliedValue вернет значение 60.

2. Замещающий класс с параметрами конструктора

Рассмотрим пример инициализации замещающего класса с параметризированным конструктором. При этом у замещаемого класса тоже параметризированный конструктор.

Базовый класс BpmReplacedClass
/// <summary>
/// Замещаемый (базовый) класс.
/// </summary>
public class BpmReplacedClass
{
	/// <summary>
	/// Свойство класса.
	/// </summary>
	public int OriginalValue { get; private set; }
	
	/// <summary>
	/// Конструктор по умолчанию.
	/// </summary>
	public BpmReplacedClass(int originalValue) {
		// Инициализация поля класса.
		OriginalValue =  originalValue;
	}
	
	/// <summary>
	/// Метод, который может быть переопределен в замещающем классе.
	/// </summary>
	public virtual int GetMultipledValue(){
		return OriginalValue * 5;
	}
}
Производный класс BpmReplacementClass
/// <summary>
/// Замещающий (производный) класс.
/// </summary>

// Атрибут Override указывается для построения дерева зависимостей замещающих и замещаемых типов.
// Атрибут указывает на то, что данный класс являются замещающим.
[BPMSoft.Core.Factories.Override]
public class BpmReplacementClass : BpmReplacedClass
{
	/// <summary>
	///  Свойство класса BpmReplacementClass
	/// </summary>
	public int AdditionalValue { get; private set; }
	
	/// <summary>
	/// Конструктор по умолчанию, который инициализирует свойство AdditionalValue значением 15.
	/// </summary>
	public BpmReplacementClass()
	{
		AdditionalValue = 15;
	}
	
	/// <summary>
	/// Конструктор с параметром, который инициализирует свойство AdditionalValue значением, переданным в качестве параметра.
	/// </summary>
	public BpmReplacementClass(int additionalValue) : base(additionalValue + 1)
	{
		AdditionalValue = additionalValue;
	}
	/// <summary>
	/// Замещение родительского метода. Метод возаращает AdditionalValue, умноженное на 3.
	/// </summary>
	public override int GetMultipliedValue()
	{
		return AdditionalValue * 3;
	}
}

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

var replacementClassWithParameters = ClassFactory.Get<BpmReplacedClass>(new ConstructorArgument("additionalValue", 10));

Переменная OriginalValue в данном случае будет содержать значение 11, а переменная AdditionalValue содержит 10. Метод GetMultipliedValue вернет значение 30.

Примеры замещения методов

После создания замещающего (производного) класса и применения атрибута [Override] можно использовать различные способы замещения методов в зависимости от структуры базового класса и задач. На практике используются три сценария: переопределение virtual-методов, сочетание [Override] с ClassFactory для подмены типа при создании, а также скрытие невиртуальных методов через new (при наличии ограничений базового кода).

1. Переопределение virtual-методов (override)

Если метод базового класса объявлен как virtual, его можно переопределить в производном классе с помощью override. Для корректного переопределения метод в наследнике обязан иметь точно такую же сигнатуру, как виртуальный метод базового класса. Вызов будет выполняться по фактическому типу объекта во время выполнения, а при необходимости можно обратиться к базовой реализации через base.MethodName().

Базовый класс
public class Shape
{
    public virtual double GetArea()
    {
        return 0;
    }
}
Производный класс
public class Rectangle : Shape
{
    public double Width { get; }
    public double Height { get; }

    public Rectangle(double width, double height)
    {
        Width = width;
        Height = height;
    }

    public override double GetArea()
    {
        return Width * Height;
    }
}
Использование
Shape s1 = new Rectangle(3, 4);
Console.WriteLine(s1.GetArea()); // 12

2. [Override] + ClassFactory.Get<T> и вызов переопределенных методов

Атрибут [Override] применяется к классу и указывает, что этот класс замещает свой базовый класс. При создании объекта через ClassFactory.Get<T>() возвращается экземпляр замещающего класса, даже если запрашивается базовый тип. Далее поведение методов определяется правилами C#: если метод базового класса является virtual, то при вызове будет использоваться реализация замещающего класса (при условии, что метод переопределен через override).

Базовый класс
public class EmailSender
{
    public virtual void SendEmail(string message)
    {
        Console.WriteLine(message);
    }
}
Замещающий класс
[Override]
public class CustomEmailSender : EmailSender
{
    public override void SendEmail(string text)
    {
        // При необходимости можно вызвать базовую реализацию:
        // base.SendEmail(text);

        Console.WriteLine($"{text} (Отправлено с особой пометкой!)");
    }
}
Использование
// Создание через фабрику — будет возвращен замещающий тип.
var sender = ClassFactory.Get<EmailSender>(); // фактически CustomEmailSender
sender.SendEmail("Привет"); // Привет (Отправлено с особой пометкой!)

// Создание напрямую — подмена не применяется.
var baseSender = new EmailSender();
baseSender.SendEmail("Привет"); // Привет

3. Скрытие невиртуальных методов (new)

Если метод базового класса не объявлен как virtual, переопределение через override невозможно. В таких случаях можно объявить в производном классе метод с той же сигнатурой и ключевым словом new, чтобы скрыть базовую реализацию.

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

Базовый класс с невиртуальным методом
public class BaseNotifier
{
    public void Notify(string message)
    {
        Console.WriteLine($"Базовое уведомление: {message}");
    }
}
Производный класс со скрытием метода
public class AdvancedNotifier : BaseNotifier
{
    public new void Notify(string message)
    {
        Console.WriteLine($"Расширенное уведомление: {message}");
    }
}
Использование
BaseNotifier notifier1 = new BaseNotifier();
BaseNotifier notifier2 = new AdvancedNotifier();
AdvancedNotifier notifier3 = new AdvancedNotifier();

notifier1.Notify("Привет!"); // Базовое уведомление: Привет!
notifier2.Notify("Привет!"); // Базовое уведомление: Привет!
notifier3.Notify("Привет!"); // Расширенное уведомление: Привет!

При совместном использовании с атрибутом [Override], данный механизм позволяет изменять поведение невиртуальных методов базовых классов без модификации исходного кода. Атрибут [Override] обеспечивает подмену типа при создании объекта через ClassFactory, а ключевое слово new скрывает реализацию метода базового класса в наследнике. Таким образом, базовый класс фактически перестает использоваться напрямую, а вся работа ведется с замещающим типом.

В данном случае замещение поведения достигается не средствами полиморфизма CLR, а за счет инфраструктурной подмены типа на этапе создания объекта и последующего скрытия базовой реализации метода.

Материал был полезен для вас?
Заявка на индивидуальный тренинг
Заявка на обучение в формате тренинга
Заявка на консультацию по обучению
Готовы сделать выбор CRM? (детальная)
Оставьте заявку, и наши эксперты бесплатно проконсультируют вас, подберут подходящую конфигурацию и рассчитают стоимость проекта.
Предпочитаемый способ связи
Готовы сделать выбор CRM?
Оставьте заявку, и наши эксперты бесплатно проконсультируют вас, подберут подходящую конфигурацию и рассчитают стоимость проекта.
Предпочитаемый способ связи
Вебинар: 25 июня в 11:00
Приглашаем вас на вебинар: BPMSoft CRM без отраслевых границ. От трейдинга до здравоохранения. Опыт и цифры
Готовы сделать выбор CRM? (детальная)
Оставьте заявку, и наши эксперты бесплатно проконсультируют вас, подберут подходящую конфигурацию и рассчитают стоимость проекта.
Предпочитаемый способ связи
Готовы сделать выбор CRM?
Оставьте заявку, и наши эксперты бесплатно проконсультируют вас, подберут подходящую конфигурацию и рассчитают стоимость проекта.
Предпочитаемый способ связи
Регистрация на мероприятие
Оставить заявку
Оставьте свои контакты и наш менеджер свяжется с Вами в ближайшее время.
Предпочитаемый способ связи
Демонстрационная версия BPMSoft
Заполните заявку для получения бесплатного доступа к демонстрационному стенду на 14 дней.
Типовое внедрение
Внедрите BPMSoft CRM в свою компанию всего за 8 рабочих дней по фиксированной цене! Заполните заявку для уточнения условий.
Предпочитаемый способ связи
Заказать презентацию
Наш менеджер свяжется с Вами в ближайшее время.
Предпочитаемый способ связи
Рассчитать стоимость
Предпочитаемый способ связи
Лучшие CRM-системы в России: выводы Фонда «Сколково»
Какие CRM выбирают крупнейшие российские компании? Скачайте исследование рынка CRM-систем 2026 от Фонда «Сколково» и TAdviser
Задать вопрос
Предпочитаемый способ связи
Есть вопросы?
Не нашли для себя подходящую вакансию, или остались вопросы?
*
Есть вопросы?
Не нашли для себя подходящую вакансию, или остались вопросы?
*
Присоединяйтесь к партнерской сети BPMSoft
Оставьте свои контакты и наш менеджер свяжется с Вами в ближайшее время
Тип партнерства*
Управление полным жизненным циклом клиента: от генерации лидов и продаж до внедрения, поддержки и продления подписки.
Разработка собственного Приложения – производного программного обеспечения, созданного на платформе BPMSoft (Базовое ПО).
Заявка на онлайн-консультации по BPMSoft
Оставьте свои контакты и наш менеджер свяжется с Вами в ближайшее время
Стать образовательным партнёром
Оставьте свои контакты и наш менеджер свяжется с Вами в ближайшее время.
Заявка на консультацию
Оставьте свои контакты и наш менеджер свяжется с Вами в ближайшее время.
Подписка
Спасибо!
Ваша заявка принята.
Спасибо!
Ваша заявка принята.
Наш сотрудник свяжется с вами в течение 1-2 рабочих дней.
Внимание!
Обнаружена ошибка.
Проверьте вашу почту
Проверьте вашу почту
Для завершения подписки перейдите по ссылке в письме, которое мы только что отправили. Если письма нет во «Входящих», проверьте папку «Спам».
MAX Подписаться
Уважаемые клиенты! Предупреждаем о случаях недобросовестной конкуренции и мошенничестве в сети Интернет.
Подробнее