← Назад к вопросам

Что такое Абстрактный класс?

1.0 Junior🔥 241 комментариев
#ООП и паттерны проектирования

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI21 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Абстрактный класс в C#

Абстрактный класс (Abstract Class) - это класс, который не может быть напрямую инстанцирован и служит базисом для других классов. Он определяет структуру и поведение, которые должны реализовать наследующие его классы.

Определение абстрактного класса

public abstract class Animal
{
    // Конкретное свойство
    public string Name { get; set; }
    
    // Конкретный метод - может быть переопределён
    public virtual void Eat()
    {
        Console.WriteLine($"{Name} is eating");
    }
    
    // Абстрактный метод - ОБЯЗАН быть реализован в наследнике
    public abstract void MakeSound();
    
    // Абстрактный метод - ОБЯЗАН быть реализован
    public abstract void Move();
}

// ОШИБКА - не можно инстанцировать абстрактный класс
var animal = new Animal();  // Compilation error!

// Наследуемый класс ДОЛЖЕН реализовать все абстрактные методы
public class Dog : Animal
{
    // ОБЯЗАТЕЛЬНО - реализация абстрактного метода
    public override void MakeSound()
    {
        Console.WriteLine($"{Name} says: Woof!");
    }
    
    // ОБЯЗАТЕЛЬНО - реализация абстрактного метода
    public override void Move()
    {
        Console.WriteLine($"{Name} runs on four legs");
    }
}

public class Bird : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine($"{Name} says: Tweet!");
    }
    
    public override void Move()
    {
        Console.WriteLine($"{Name} flies in the sky");
    }
}

// Теперь можно инстанцировать конкретные классы
var dog = new Dog { Name = "Rex" };
dog.MakeSound();   // Rex says: Woof!
dog.Eat();         // Rex is eating
dog.Move();        // Rex runs on four legs

var bird = new Bird { Name = "Tweety" };
bird.MakeSound();  // Tweety says: Tweet!
bird.Move();       // Tweety flies in the sky

Различия между Interface и Abstract Class

// ИНТЕРФЕЙС - контракт ТОЛЬКО с методами
public interface IAnimal
{
    void MakeSound();
    void Move();
}

// АБСТРАКТНЫЙ КЛАСС - может иметь реализацию и состояние
public abstract class Animal
{
    // Может иметь поля и конкретное поведение
    public string Name { get; set; }
    public int Age { get; set; }
    
    // Конкретные методы с реализацией
    public virtual void Eat()
    {
        Console.WriteLine("Eating...");
    }
    
    // Абстрактные методы - должны быть реализованы
    public abstract void MakeSound();
    public abstract void Move();
}

Таблица различий:

АспектInterfaceAbstract Class
ИнстанцированиеНельзяНельзя
Состояние (поля)НетДа
Конкретные методыНет (C# 8.0+)Да
НаследованиеМножественноеОдиночное
Модификаторы доступаpublic/internalany
КонструкторыНетДа
ДеструкторыНетДа

Практические примеры

1. Платёжные системы

public abstract class PaymentProcessor
{
    // Общее состояние
    public string MerchantId { get; set; }
    public decimal Commission { get; set; }
    
    // Общая реализация
    public decimal CalculateTotal(decimal amount)
    {
        return amount + (amount * Commission / 100);
    }
    
    // Абстрактные методы - каждая система реализует по-своему
    public abstract void Authenticate();
    public abstract Task<bool> ProcessPaymentAsync(decimal amount);
    public abstract void Refund(string transactionId);
}

public class StripeProcessor : PaymentProcessor
{
    public override void Authenticate()
    {
        // Аутентификация в Stripe
    }
    
    public override async Task<bool> ProcessPaymentAsync(decimal amount)
    {
        var total = CalculateTotal(amount);
        // Логика Stripe
        return true;
    }
    
    public override void Refund(string transactionId)
    {
        // Возврат Stripe
    }
}

public class PayPalProcessor : PaymentProcessor
{
    public override void Authenticate()
    {
        // Аутентификация в PayPal
    }
    
    public override async Task<bool> ProcessPaymentAsync(decimal amount)
    {
        var total = CalculateTotal(amount);
        // Логика PayPal
        return true;
    }
    
    public override void Refund(string transactionId)
    {
        // Возврат PayPal
    }
}

2. Логирование

public abstract class Logger
{
    // Общее состояние
    protected LogLevel MinimumLevel { get; set; }
    
    // Конкретный метод - использует абстрактный
    public void Log(string message, LogLevel level)
    {
        if (level >= MinimumLevel)
        {
            WriteLog(message, level);
        }
    }
    
    // Абстрактный метод
    protected abstract void WriteLog(string message, LogLevel level);
}

public class ConsoleLogger : Logger
{
    protected override void WriteLog(string message, LogLevel level)
    {
        Console.ForegroundColor = level switch
        {
            LogLevel.Error => ConsoleColor.Red,
            LogLevel.Warning => ConsoleColor.Yellow,
            _ => ConsoleColor.White
        };
        
        Console.WriteLine($"[{level}] {message}");
    }
}

public class FileLogger : Logger
{
    private readonly string _filePath;
    
    public FileLogger(string filePath)
    {
        _filePath = filePath;
    }
    
    protected override void WriteLog(string message, LogLevel level)
    {
        var logMessage = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [{level}] {message}";
        File.AppendAllText(_filePath, logMessage + Environment.NewLine);
    }
}

public class CloudLogger : Logger
{
    private readonly ICloudService _cloudService;
    
    public CloudLogger(ICloudService cloudService)
    {
        _cloudService = cloudService;
    }
    
    protected override void WriteLog(string message, LogLevel level)
    {
        _cloudService.SendLogAsync(message, level);
    }
}

public enum LogLevel
{
    Debug = 1,
    Info = 2,
    Warning = 3,
    Error = 4
}

3. Репозитории данных

public abstract class Repository<T> where T : class
{
    protected DbContext DbContext { get; set; }
    
    // Конкретные методы
    public virtual async Task<T> GetByIdAsync(int id)
    {
        return await DbContext.Set<T>().FindAsync(id);
    }
    
    public virtual async Task<IEnumerable<T>> GetAllAsync()
    {
        return await DbContext.Set<T>().ToListAsync();
    }
    
    // Абстрактные методы для специфичной логики
    public abstract Task<T> GetWithRelatedAsync(int id);
    public abstract IQueryable<T> ApplyFilters(IQueryable<T> query, FilterParams filters);
}

public class UserRepository : Repository<User>
{
    public override async Task<User> GetWithRelatedAsync(int id)
    {
        return await DbContext.Users
            .Include(u => u.Orders)
            .Include(u => u.Profile)
            .FirstOrDefaultAsync(u => u.Id == id);
    }
    
    public override IQueryable<User> ApplyFilters(IQueryable<User> query, FilterParams filters)
    {
        if (!string.IsNullOrEmpty(filters.Name))
            query = query.Where(u => u.Name.Contains(filters.Name));
        
        if (filters.MinAge.HasValue)
            query = query.Where(u => u.Age >= filters.MinAge);
        
        return query;
    }
}

Ключевые особенности

1. Абстрактные методы ОБЯЗАТЕЛЬНЫ для реализации

public abstract class Shape
{
    public abstract double GetArea();  // ОБЯЗАТЕЛЕН
}

public class Circle : Shape
{
    private double _radius;
    
    public override double GetArea()  // ОБЯЗАТЕЛЬНО переопределить
    {
        return Math.PI * _radius * _radius;
    }
}

// Ошибка - не реализована GetArea()
public class Square : Shape  // Compilation error!
{
    // ...
}

2. Конкретные методы можно переопределить

public abstract class Vehicle
{
    public virtual void Start()
    {
        Console.WriteLine("Starting engine...");
    }
    
    public abstract void Drive();
}

public class Car : Vehicle
{
    // Переопределяем конкретный метод
    public override void Start()
    {
        Console.WriteLine("Car engine started");
        base.Start();
    }
    
    // Реализуем абстрактный метод
    public override void Drive()
    {
        Console.WriteLine("Car is driving");
    }
}

3. Конструкторы в абстрактных классах

public abstract class Employee
{
    public string Name { get; set; }
    public decimal Salary { get; set; }
    
    // Конструктор
    protected Employee(string name, decimal salary)
    {
        Name = name;
        Salary = salary;
    }
    
    public abstract decimal CalculateBonus();
}

public class Manager : Employee
{
    public Manager(string name, decimal salary) : base(name, salary)
    {
    }
    
    public override decimal CalculateBonus()
    {
        return Salary * 0.2m;  // 20% бонус для менеджеров
    }
}

Когда использовать абстрактный класс

Используй, если:

  • Нужно общее состояние (поля)
  • Нужна конкретная реализация некоторых методов
  • Классы тесно связаны и используют общий код
  • Нужны конструкторы или деструкторы
  • Нужна защита (protected, private члены)

Не используй, если:

  • Только интерфейс (контракт методов)
  • Классы не связаны
  • Нужно множественное наследование

Заключение

Абстрактный класс - это мощный инструмент для:

  • Определения общей структуры для группы классов
  • Обеспечения кода многоуровневой иерархией
  • Создания контрактов, которые ОБЯЗАНЫ реализовать потомки
  • Совместного использования конкретного кода между классами

Он более мощный, чем интерфейс, но более ограничивающий (одиночное наследование). Выбирай правильный инструмент для задачи.