Лого на 91. НЕГ „Проф. Константин Гълъбов“

Модул 1 · Урок 13

Интерфейси и полиморфизъм

Договор без имплементация. Полиморфизъм — един и същ метод работи различно за различни типове.

Какво е интерфейс

Интерфейсът е „договор" — списък с методи и свойства, които всеки клас, който го имплементира, трябва да предостави. Не съдържа никаква логика — само сигнатури.

interface IShape
{
    double Area();
    double Perimeter();
}

class Circle : IShape
{
    public double Radius;
    public double Area() => Math.PI * Radius * Radius;
    public double Perimeter() => 2 * Math.PI * Radius;
}

class Rectangle : IShape
{
    public double Width, Height;
    public double Area() => Width * Height;
    public double Perimeter() => 2 * (Width + Height);
}

Бележка

По конвенция имената на интерфейсите в C# започват с главно IIShape, IComparable, IDisposable.

Защо интерфейси, ако имаме абстрактни класове

Абстрактен класИнтерфейс
Може ли да съдържа логика?Да — обикновени методи + полетаНе (или само default методи в нови версии)
Колко наследяваш?Само ЕДИН (single inheritance)Колкото искаш (multi inheritance)
Кога ползваш?Споделена логика между сходни класовеДоговор/способност, която сходни-или-различни класове предоставят
ПримерAnimal → Dog, CatIComparable, IDisposable, IEnumerable

Ключово

Един клас може да наследи само един родителски клас, но да имплементира колкото си иска интерфейси:

class Bird : Animal, IFlyable, IComparable<Bird> { ... }

Полиморфизъм

Полиморфизъм = „един интерфейс, много форми". Един и същ метод се държи различно в зависимост от конкретния тип на обекта.

IShape[] shapes = {
    new Circle { Radius = 5 },
    new Rectangle { Width = 3, Height = 4 }
};

foreach (var s in shapes)
{
    Console.WriteLine($"Площ: {s.Area():F2}, периметър: {s.Perimeter():F2}");
}
// Площ: 78.54, периметър: 31.42
// Площ: 12.00, периметър: 14.00

Същият код работи и за Circle, и за Rectangle — програмата НЕ знае точния тип, само знае, че всеки IShape има Area и Perimeter.

Стандартни интерфейси в .NET

IComparable — сравнимост

class Student : IComparable<Student>
{
    public string Name;
    public double Grade;
    public int CompareTo(Student other) => Grade.CompareTo(other.Grade);
}

var arr = new Student[] {
    new Student { Name = "Иван", Grade = 5.50 },
    new Student { Name = "Мария", Grade = 6.00 },
    new Student { Name = "Петър", Grade = 4.20 }
};
Array.Sort(arr);   // подреждa по Grade — благодарение на CompareTo

IEnumerable — за foreach

Всеки тип, който имплементира IEnumerable<T>, може да се обхожда с foreach. Това правят масивите,List<T>, и собствените ти класове, ако пожелаеш.

IDisposable — освобождаване на ресурси

Класове, които държат файлове, връзки с БД, мрежови сокети, имплементират IDisposable с метод Dispose(). Когато ги ползваш в using блок, Dispose се извиква автоматично в края.

Параметър от тип интерфейс

Силата на интерфейсите е, че пишеш кода срещу договора, не срещу конкретен клас:

static void Describe(IShape shape)
{
    Console.WriteLine($"Тази форма има площ {shape.Area():F2}");
}

Describe(new Circle { Radius = 5 });        // работи
Describe(new Rectangle { Width = 3, Height = 4 });   // също работи
// А утре някой добави Triangle : IShape — Describe пак ще работи без промяна!

Принцип на отвореност / затвореност

Кодът трябва да е отворен за разширение (добавяме нови типове) и затворен за модификация (без да пипаме съществуващото). Интерфейсите са основният механизъм за това.

Какво трябва да запомниш

  • Интерфейс = договор (списък методи без имплементация).
  • Един клас може да имплементира няколко интерфейса.
  • Полиморфизъм = пишеш срещу интерфейса, работи за всеки конкретен тип.
  • Стандартни: IComparable, IEnumerable, IDisposable.
  • Имена с префикс I по конвенция.