Какие в C# существуют способы расширения поведения класса?

«Какие в C# существуют способы расширения поведения класса?» — вопрос из категории C# Core, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В C# есть несколько основных паттернов и языковых средств для расширения функциональности классов:

  1. Наследование (Inheritance) Классический способ создания иерархии "is-a". Новый класс наследует поля, свойства и методы базового, может их переопределять или добавлять новые.

    public class Vehicle { public void StartEngine() { } }
    public class Car : Vehicle { public void EnableAirConditioning() { } } // Car IS-A Vehicle
  2. Композиция (Composition) Принцип "has-a". Поведение расширяется за счёт включения экземпляров других классов и делегирования им вызовов. Это часто более гибкая альтернатива наследованию.

    public class Logger { public void Log(string msg) => Console.WriteLine(msg); }
    public class OrderProcessor {
        private readonly Logger _logger; // Композиция
        public OrderProcessor(Logger logger) { _logger = logger; }
        public void Process() { _logger.Log("Processing started..."); }
    }
  3. Методы расширения (Extension Methods) Позволяют добавлять методы к существующим типам (даже sealed или из сторонних библиотек) без изменения их исходного кода или создания подкласса.

    public static class DateTimeExtensions {
        public static bool IsWeekend(this DateTime date) => date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday;
    }
    // Использование:
    bool isWeekend = DateTime.Now.IsWeekend();
  4. Паттерн Декоратор (Decorator Pattern) Структурный паттерн, который динамически добавляет объекту новую функциональность, оборачивая его в другой объект с тем же интерфейсом.

    public interface INotifier { void Send(string message); }
    public class EmailNotifier : INotifier { public void Send(string msg) => Console.WriteLine($"Email: {msg}"); }
    public class SmsNotifierDecorator : INotifier {
        private readonly INotifier _wrappedNotifier;
        public SmsNotifierDecorator(INotifier notifier) { _wrappedNotifier = notifier; }
        public void Send(string msg) {
            _wrappedNotifier.Send(msg); // Базовая функциональность
            Console.WriteLine($"SMS: {msg}"); // Добавленная функциональность
        }
    }
  5. Частичные классы (Partial Classes) Позволяют разделить определение одного класса на несколько файлов. Часто используется в сгенерированном коде (например, WinForms, WPF), чтобы отделить пользовательскую логику от автоматически генерируемой.

    // Файл: MyClass.Generated.cs (автосгенерирован)
    public partial class MyClass { public int Id { get; set; } }
    // Файл: MyClass.Custom.cs (ручная разработка)
    public partial class MyClass { public string GetDisplayName() => $"ID: {Id}"; }

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