В чём разница между абстрактным классом и интерфейсом?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между абстрактным классом и интерфейсом
Это два разных механизма для определения контрактов в 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; }
}
Этот подход обеспечивает максимальную гибкость и переиспользование кода.