Ответ
В C# есть несколько основных паттернов и языковых средств для расширения функциональности классов:
-
Наследование (Inheritance) Классический способ создания иерархии "is-a". Новый класс наследует поля, свойства и методы базового, может их переопределять или добавлять новые.
public class Vehicle { public void StartEngine() { } } public class Car : Vehicle { public void EnableAirConditioning() { } } // Car IS-A Vehicle -
Композиция (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..."); } } -
Методы расширения (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(); -
Паттерн Декоратор (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}"); // Добавленная функциональность } } -
Частичные классы (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}"; }
Выбор подхода зависит от контекста: наследование для иерархий типов, композиция для гибкости, методы расширения для утилитарных функций, декоратор для динамического добавления обязанностей.
Ответ 18+ 🔞
Давай разберём, как в C# можно натянуть новые фичи на старый класс, чтобы он не просто работал, а ещё и не развалился при этом. Способов — как говна за баней, выбирай любой.
Наследование (Inheritance) Это классика, типа «я твой папа». Берёшь готовый класс и делаешь его наследника, который всё от родителя получает, а ещё может своё добавить или что-то переписать. Работает по принципу «является» (is-a).
public class Vehicle { public void StartEngine() { } }
public class Car : Vehicle { public void EnableAirConditioning() { } } // Car — это Vehicle, только с кондишном
Но тут главное не переборщить, а то получишь иерархию глубже, чем проблемы у алкоголика, и потом сам в ней запутаешься.
Композиция (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("Начинаю процесс..."); } // Работу спихнул на логгер
}
Часто это надёжнее, чем наследование — меньше связанности, как будто не родственники, а просто соседи.
Методы расширения (Extension Methods)
А это вообще магия, ебать! Хочешь добавить метод к классу, который тебе не принадлежит? К string, к DateTime, к чему угодно, даже к sealed? Пожалуйста! Пишешь статический метод с this перед первым параметром — и всё, теперь это будто бы родной метод класса. Никакого наследования, никакого доступа к приватным полям — чистая утилитарная подпорка.
public static class DateTimeExtensions {
public static bool IsWeekend(this DateTime date) => date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday;
}
// Используешь как родной:
bool isWeekend = DateTime.Now.IsWeekend(); // И вроде бы у DateTime всегда был такой метод!
Идеально для всяких вспомогательных функций, чтобы не плодить хелперы по всему проекту.
Паттерн Декоратор (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); // Сначала отправляем email (базовая фича)
Console.WriteLine($"SMS: {msg}"); // А потом ещё и смс (дополнительная фича)
}
}
// Использование:
INotifier notifier = new SmsNotifierDecorator(new EmailNotifier());
notifier.Send("Ты кто такой? Давай, до свидания!"); // Уйдёт и email, и смс
Красота в том, что оборачивать можно сколько угодно раз, хоть в десять слоёв, как луковицу.
Частичные классы (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}"; }
Удобно, когда не хочешь лезть в автогенерацию, но нужно класс дополнить.
Итог Какой способ выбрать? Да смотри по ситуации, блядь!
- Наследование — когда есть чёткая иерархия «является».
- Композиция — когда нужно собрать объект из частей, гибко и без тесных родственных связей.
- Методы расширения — чтобы добавить утилитарный метод к чужому или системному типу.
- Декоратор — когда нужно добавлять обязанности динамически, на лету.
- Частичные классы — в основном для работы с автогенерируемым кодом.
Главное — понимать, зачем ты это делаешь, а не просто тыкать первое, что пришло в голову, как обезьяна в клавиатуру.