Към съдържанието
wannacode

Какъв е всъщност?

Проблем

Само някои могат да се зареждат

Фирмата има автопарк от обикновени и електрически коли. Всяка вечер електрическите трябва да се заредят. ElectricCar наследява Car и добавя Charge(), затова всички са в един масив Car[]:

Car[] fleet =
{
new Car("Шкода"),
new ElectricCar("Тесла", 35),
new Car("Дачия"),
new ElectricCar("Нисан", 60)
};
foreach (Car c in fleet)
{
c.Charge();
}

Програмата не се компилира:

„'Car' does not contain a definition for 'Charge'“.

От урока за полиморфизма знаеш защо: типът на променливата решава какво можеш да извикаш. c е Car, а в Car няма зареждане.

Обикновената кола наистина не може да се зареди. Трябва да питаш всяка кола: „ти електрическа ли си?“

Идеята

Питай обекта какъв е

foreach (Car c in fleet)
{
if (c is ElectricCar e)
{
e.Charge();
Console.WriteLine(e.Brand + ": заредена до " + e.Battery + "%");
}
}
Изход
Тесла: заредена до 100%
Нисан: заредена до 100%

c is ElectricCar e прави две неща наведнъж:

  • проверява дали обектът, към който сочи c, е ElectricCar;
  • ако е, създава променлива e от тип ElectricCar, която сочи същия обект.

През e вече можеш да викаш Charge(). Колите остават същите. Само поглеждаш към обекта през по-конкретен тип.

Това се казва проверка на типа. Преходът от родителския тип към типа на наследника се казва превръщане надолу (downcast): надолу по веригата Object → Car → ElectricCar.

e съществува само вътре в if. Извън него обектът е пак просто Car.

foreach (Car c in fleet)
{
if (c is ElectricCar e)
{
e.Charge();
Console.WriteLine(e.Brand + ": заредена до " + e.Battery + "%");
}
}
Стъпка по стъпка, с думи
  1. fleet е масив от Car: 2 обикновени и 2 електрически коли.
  2. c сочи Шкода: is дава „не“. Тялото на if се прескача.
  3. c сочи Тесла: is дава „да“. e е втора стрелка към същата кола и я зарежда.
  4. c сочи Дачия: is дава „не“. Тялото на if се прескача.
  5. c сочи Нисан: is дава „да“. e е втора стрелка към същата кола и я зарежда.
  6. Колите не се променят. Проверката само поглежда към обекта през по-конкретен тип.

Пробвай

Кола ли е?

Какво ще отпечата програмата?

Car c = new ElectricCar("Тесла", 35);
Car d = new Car("Шкода");
Console.WriteLine(c is Car);
Console.WriteLine(c is ElectricCar);
Console.WriteLine(d is ElectricCar);
Отговор

Идеята

Другите записи, които ще срещнеш

Освен is има и as. То опитва превръщането и ако обектът не е от този тип, дава null вместо грешка:

ElectricCar e = fleet[1] as ElectricCar;
if (e != null)
{
e.Charge();
Console.WriteLine(e.Brand + ": " + e.Battery + "%");
}
ElectricCar none = fleet[0] as ElectricCar;
Console.WriteLine(none == null);
Изход
Тесла: 100%
True

fleet[0] е „Шкода“, затова as дава null. Тогава трябва да провериш за null, преди да викаш методи. С is ElectricCar e проверката и превръщането са на едно място, затова е по-кратко.

По-точно, за любопитните
  • Класическото превръщане (ElectricCar)c също съществува. Ако обектът не е ElectricCar, програмата спира с InvalidCastException.
  • Проверката е винаги грешна за null: null is Car е false. Затова след успешен is никога нямаш null.
  • Проверката работи и с интерфейси: a is ISwimmable s пита дали класът на обекта реализира договора.

Пробвай

Намери грешката

Програмата брои обикновените и електрическите коли. В автопарка има две от всеки вид, а тя печата:

Изход
Обикновени: 4, електрически: 0

На кой ред е грешката?

Избери реда с грешката.

Идеята

Проверка или метод?

Искаш да отпечаташ с какво се движи всяка кола. С проверка на типа става:

foreach (Car c in fleet)
{
if (c is ElectricCar)
{
Console.WriteLine(c.Brand + ": ток");
}
else
{
Console.WriteLine(c.Brand + ": бензин");
}
}

Но това е заплатата с if по роля от урока за полиморфизма. Всяка кола има енергия, само че различна. Това е работа за метод в Car, презаписан в ElectricCar:

// в Car
public virtual string Energy()
{
return "бензин";
}
// в ElectricCar
public override string Energy()
{
return "ток";
}
foreach (Car c in fleet)
{
Console.WriteLine(c.Brand + ": " + c.Energy());
}
Изход
Шкода: бензин
Тесла: ток
Дачия: бензин
Нисан: ток

Утре идва хибридна кола. С метода пишеш нов клас. С проверките търсиш всеки if в програмата.

Правилото:

  • Всеки обект го прави, но по свой начин: метод в родителя и презаписване.
  • Само някои обекти го могат (зареждане, педали): проверка на типа.

Пробвай

Кое липсва?

foreach (Car c in fleet)
{
    if (c  ElectricCar e)
    {
        e.Charge();
    }
}

Накратко

Четирите стълба на ООП

С този урок модулите „Класове и обекти“ и „Наследяване и полиморфизъм“ са минати. Обектно-ориентираното програмиране стои на четири идеи. Вече ползваш всичките:

  • Капсулация. Данните на обекта са скрити, а отвън се стига до тях само през методи, които пазят правилата. Account държи баланса private и дава само Deposit() и Withdraw(). Капсулация
  • Наследяване. Общото е на едно място, в родителя, а наследникът добавя своето. Manager е Employee с бонус. Наследяване
  • Полиморфизъм. Едно извикване, различно поведение според обекта. e.MonthlyPay() в един цикъл смята заплатата на служител, мениджър и стажант. Полиморфизъм
  • Абстракция. Казваш какво трябва да може обектът, а как решава всеки клас. Абстрактният Shape обещава лице, интерфейсът IPayable обещава сума за плащане. Абстрактни класове, Интерфейси

Упражнение

Какъв е всъщност?

Реши задачите в редактора си.

1. Паркинг. Масив SimpleCar[] с обикновени и семейни коли (FamilyCar наследява SimpleCar). За всяка отпечатай какъв е типът ѝ. Внимавай с реда на проверките.

Изход
Това е SimpleCar
Това е FamilyCar
Това е SimpleCar

2. Танци. Масив DanceMove[] с обикновени и брейк движения. За брейк движенията отпечатай и сложността. За нея ти трябва променливата от проверката.

Изход
moonwalk: DanceMove
windmill: BreakDanceMove, сложност 8
салса: DanceMove

3. Автопарк. Вземи Car и ElectricCar от упражнението в урока „Наследяване“. В масив Car[] сложи „Шкода“ с 40 л, „Тесла“ с ефективност 5, „Дачия“ с 5 л и „Нисан“ с ефективност 4. Всяка да измине 100 км: електрическите с DriveElectric, останалите с Drive. После отпечатай километрите и горивото или батерията.

Изход
Шкода: 100 км, гориво 30 л
Тесла: 100 км, батерия 80%
Горивото не стига за 100 км
Дачия: 0 км, гориво 5 л
Нисан: 100 км, батерия 75%

4. Зоопарк. Абстрактен клас Animal със Sound(), наследници Cat, Dog и Fish. Само Fish реализира ISwimmable с Swim(). В цикъл през животните извикай звука на всяко, а на тези, които могат да плуват, и Swim().

Изход
Мяу, мяу!
Бау, бау!
Рибата мълчи.
Рибата плува.

Още задачи

  • Педали. В упражнението от урока „Абстрактни класове“ garage[1].Pedal() не се компилираше. Поправи го с проверка на типа: в цикъла през Vehicle[] всяко превозно средство потегля, а велосипедът и върти педали.
  • Плащания по вид. От урока „Интерфейси“: в масива IPayable[] има служители и фактури. Отпечатай отделно сумата за заплати и сумата за фактури. За Иван, Мария и двете фактури: 5500 € заплати и 2140 € фактури.

Тест

Провери се

1. c is ElectricCar e е вярно. Какво е e?
2. c сочи ElectricCar. Колко е c is Car?
3. Проверяваш дали колата е Car или ElectricCar. В какъв ред?
4. В програмата има много if-ове „ако е ElectricCar, прави едно, иначе друго“. Какво подсказва това?
5. c сочи обикновена кола. Какво дава c as ElectricCar?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·