В C# существует несколько способов расширения поведения классов без изменения их исходного кода. Это позволяет сделать программу более гибкой, масштабируемой и удобной для поддержки. Основные подходы включают использование наследования, интерфейсов, абстрактных классов, паттернов проектирования и других механизмов.
1. Наследование (Inheritance)
Наследование позволяет создавать производные классы, которые расширяют или изменяют поведение базового класса.
Пример:
public class Animal
{
public virtual void Speak()
{
Console.WriteLine("Animal is making a sound");
}
}
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Dog is barking");
}
}
Когда использовать
- Когда новый класс логически является подтипом существующего.
- Когда требуется переопределение поведения базового класса.
2. Интерфейсы (Interfaces)
Интерфейсы определяют контракт для классов, обеспечивая гибкость и возможность расширения.
Пример:
public interface IMovable
{
void Move();
}
public class Car : IMovable
{
public void Move()
{
Console.WriteLine("Car is moving");
}
}
Когда использовать
- Когда необходимо описать поведение без привязки к конкретной реализации.
- Когда требуется реализация нескольких интерфейсов.
3. Абстрактные классы (Abstract Classes)
Абстрактные классы позволяют определить базовое поведение с возможностью частичной или полной реализации методов.
Пример:
public abstract class Shape
{
public abstract void Draw();
public void ShowInfo()
{
Console.WriteLine("This is a shape");
}
}
public class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a circle");
}
}
Когда использовать
- Когда требуется базовая функциональность для наследников.
- Когда некоторые методы должны быть реализованы в производных классах.
4. Декоратор (Decorator Pattern)
Позволяет динамически добавлять новое поведение объектам без изменения их кода.
Пример:
public interface IMessage
{
void Send();
}
public class SimpleMessage : IMessage
{
public void Send()
{
Console.WriteLine("Sending a simple message");
}
}
public class EncryptedMessage : IMessage
{
private IMessage _message;
public EncryptedMessage(IMessage message)
{
_message = message;
}
public void Send()
{
Console.WriteLine("Encrypting message...");
_message.Send();
}
}
Когда использовать
- Когда требуется динамическое изменение поведения объекта.
- Когда необходимо избегать жесткой привязки к конкретным классам.
5. Расширяющие методы (Extension Methods)
Позволяют добавлять методы к существующим классам без их изменения.
Пример:
public static class StringExtensions
{
public static string ToUpperFirstLetter(this string str)
{
if (string.IsNullOrEmpty(str)) return str;
return char.ToUpper(str[0]) + str.Substring(1);
}
}
// Использование:
string name = "john";
Console.WriteLine(name.ToUpperFirstLetter()); // Output: John
Когда использовать
- Когда необходимо добавить методы к сторонним или неизменяемым классам.
- Когда требуется расширение стандартных типов (
string
,List<T>
и др.).
6. Делегаты и события (Delegates & Events)
Позволяют изменять поведение класса через подписку на события.
Пример:
public class Button
{
public event Action Clicked;
public void Click()
{
Clicked?.Invoke();
}
}
// Использование:
Button button = new Button();
button.Clicked += () => Console.WriteLine("Button clicked!");
button.Click();
Когда использовать
- Когда требуется передавать функции в качестве параметров.
- Когда необходимо реализовать механизм событий.
7. Стратегия (Strategy Pattern)
Позволяет подменять поведение объекта в зависимости от контекста.
Пример:
public interface IPaymentStrategy
{
void Pay();
}
public class CreditCardPayment : IPaymentStrategy
{
public void Pay()
{
Console.WriteLine("Paid with credit card");
}
}
public class PayPalPayment : IPaymentStrategy
{
public void Pay()
{
Console.WriteLine("Paid with PayPal");
}
}
public class PaymentProcessor
{
private IPaymentStrategy _strategy;
public PaymentProcessor(IPaymentStrategy strategy)
{
_strategy = strategy;
}
public void ProcessPayment()
{
_strategy.Pay();
}
}
Когда использовать
- Когда требуется изменение алгоритма работы в зависимости от условий.
- Когда необходимо инкапсулировать несколько способов выполнения задачи.