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

В чём разница между абстрактным классом и интерфейсом?

1.0 Junior🔥 191 комментариев
#C# и ООП

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

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

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

Разница между абстрактным классом и интерфейсом

Это два разных механизма для определения контрактов в C#. Хотя оба определяют, что класс должен реализовать, они используются в разных сценариях и имеют принципиальные различия.

Основные различия

1. Состояние и данные

Абстрактный класс может содержать поля (состояние) и инициализировать их:

public abstract class Character
{
    protected string name;  // Состояние
    protected float health = 100f;  // Инициализированное поле
    
    public abstract void Attack();
}

Интерфейс не может содержать поля (только свойства с реализацией в C# 8.0+):

public interface ICharacter
{
    void Attack();
    // Нельзя добавить: protected string name;
}

2. Реализация методов

Абстрактный класс может иметь полную реализацию некоторых методов:

public abstract class Character
{
    public virtual void PrintStats()
    {
        Debug.Log("Stats printed");
    }
    
    public abstract void Attack();  // Только сигнатура
}

Интерфейс в C# 8.0+ может иметь default реализацию, но это редко используется:

public interface ICharacter
{
    void Attack();  // Только сигнатура
    void PrintStats() { Debug.Log("Stats"); }  // Default реализация (C# 8.0+)
}

3. Модификаторы доступа

Абстрактный класс поддерживает все модификаторы (protected, private, internal):

public abstract class Character
{
    protected abstract void PrivateAttack();  // Protected
    private void HiddenMethod() { }  // Private
}

Интерфейс имеет только public методы (до C# 8.0 только public, сейчас поддерживает private):

public interface ICharacter
{
    void Attack();  // Всегда public
}

4. Наследование и реализация

Абстрактный класс — наследование (is-a отношение):

public class Knight : Character  // Наследование
{
    public override void Attack()
    {
        Debug.Log("Knight attacks with sword");
    }
}

Интерфейс — реализация (can-do отношение):

public class Knight : ICharacter, IDamageable  // Реализация интерфейсов
{
    public void Attack() { }
    public void TakeDamage(float damage) { }
}

5. Множественное наследование

Абстрактный класс не поддерживает множественное наследование (можно наследовать только один):

public class Knight : Character  // Только один родительский класс
{
}

Интерфейс поддерживает множественную реализацию:

public class Knight : ICharacter, IDamageable, IHealable  // Несколько интерфейсов
{
}

6. Конструкторы

Абстрактный класс может иметь конструкторы:

public abstract class Character
{
    protected Character(string name)  // Конструктор
    {
        this.name = name;
    }
}

Интерфейс не имеет конструкторов:

public interface ICharacter
{
    // Конструкторы запрещены
}

Когда что использовать

Используй абстрактный класс когда:

  • Нужно определить состояние (поля)
  • Нужны конструкторы для инициализации
  • Логически связанные классы с общей иерархией
  • Нужны защищённые или приватные методы
  • Есть shared функциональность, которую наследники должны использовать

Используй интерфейс когда:

  • Определяешь контракт (что класс может делать)
  • Нужна множественная реализация
  • Классы не имеют логического отношения наследования
  • Нужна гибкость в имплементации
  • Определяешь способность (например, IDamageable, IPoolable)

Пример из игры

// Абстрактный класс для иерархии персонажей
public abstract class Character
{
    protected float health;
    protected string characterName;
    
    protected Character(string name)
    {
        characterName = name;
        health = 100f;
    }
    
    public abstract void Attack();
    
    public virtual void TakeDamage(float damage)
    {
        health -= damage;
    }
}

// Интерфейсы для способностей
public interface IHealable
{
    void Heal(float amount);
}

public interface IPoolable
{
    void Reset();
}

// Конкретный класс
public class Player : Character, IHealable, IPoolable
{
    public override void Attack() { }
    public void Heal(float amount) { health += amount; }
    public void Reset() { health = 100f; }
}

Этот подход обеспечивает максимальную гибкость и переиспользование кода.

В чём разница между абстрактным классом и интерфейсом? | PrepBro