Какъв е всъщност?
Проблем
Само някои могат да се зареждат
Фирмата има автопарк от обикновени и електрически коли. Всяка вечер електрическите трябва да се заредят. 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 + "%"); }}Стъпка по стъпка, с думи
- fleet е масив от Car: 2 обикновени и 2 електрически коли.
- c сочи Шкода: is дава „не“. Тялото на if се прескача.
- c сочи Тесла: is дава „да“. e е втора стрелка към същата кола и я зарежда.
- c сочи Дачия: is дава „не“. Тялото на if се прескача.
- c сочи Нисан: is дава „да“. e е втора стрелка към същата кола и я зарежда.
- Колите не се променят. Проверката само поглежда към обекта през по-конкретен тип.
Пробвай
Кола ли е?
Какво ще отпечата програмата?
Car c = new ElectricCar("Тесла", 35);Car d = new Car("Шкода");Console.WriteLine(c is Car);Console.WriteLine(c is ElectricCar);Console.WriteLine(d is ElectricCar);Електрическата кола е и кола, затова първата проверка е вярна. Проверката не пита „точно Car ли е?“, а „може ли обектът да се ползва като Car?“. Обикновената кола d не е електрическа, затова последната проверка е грешна.
Идеята
Другите записи, които ще срещнеш
Освен 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%Truefleet[0] е „Шкода“, затова as дава null. Тогава трябва да провериш за null, преди да викаш методи. С is ElectricCar e проверката и превръщането са на едно място, затова е по-кратко.
По-точно, за любопитните
- Класическото превръщане
(ElectricCar)cсъщо съществува. Ако обектът не еElectricCar, програмата спира сInvalidCastException. - Проверката е винаги грешна за
null:null is Carеfalse. Затова след успешенisникога нямашnull.
- Проверката работи и с интерфейси:
a is ISwimmable sпита дали класът на обекта реализира договора.
Пробвай
Намери грешката
Програмата брои обикновените и електрическите коли. В автопарка има две от всеки вид, а тя печата:
Обикновени: 4, електрически: 0На кой ред е грешката?
Избери реда с грешката.
Всяка електрическа кола е и Car, затова първата проверка е вярна за всички четири коли. До втората програмата не стига никога. Проверявай първо по-конкретния тип: ако е ElectricCar, брой електрическа, иначе обикновена.
Идеята
Проверка или метод?
Искаш да отпечаташ с какво се движи всяка кола. С проверка на типа става:
foreach (Car c in fleet){ if (c is ElectricCar) { Console.WriteLine(c.Brand + ": ток"); } else { Console.WriteLine(c.Brand + ": бензин"); }}Но това е заплатата с if по роля от урока за полиморфизма. Всяка кола има енергия, само че различна. Това е работа за метод в Car, презаписан в ElectricCar:
// в Carpublic virtual string Energy(){ return "бензин";}
// в ElectricCarpublic 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();
}
}is: проверява типа и, ако е верен, създава e.Накратко
Четирите стълба на ООП
С този урок модулите „Класове и обекти“ и „Наследяване и полиморфизъм“ са минати. Обектно-ориентираното програмиране стои на четири идеи. Вече ползваш всичките:
- Капсулация. Данните на обекта са скрити, а отвън се стига до тях само през методи, които пазят правилата.
Accountдържи баланса private и дава самоDeposit()иWithdraw(). Капсулация - Наследяване. Общото е на едно място, в родителя, а наследникът добавя своето.
ManagerеEmployeeс бонус. Наследяване - Полиморфизъм. Едно извикване, различно поведение според обекта.
e.MonthlyPay()в един цикъл смята заплатата на служител, мениджър и стажант. Полиморфизъм - Абстракция. Казваш какво трябва да може обектът, а как решава всеки клас. Абстрактният
Shapeобещава лице, интерфейсът IPayable обещава сума за плащане. Абстрактни класове, Интерфейси
Упражнение
Какъв е всъщност?
Реши задачите в редактора си.
1. Паркинг. Масив SimpleCar[] с обикновени и семейни коли (FamilyCar наследява SimpleCar). За всяка отпечатай какъв е типът ѝ. Внимавай с реда на проверките.
Това е SimpleCarТова е FamilyCarТова е SimpleCar2. Танци. Масив DanceMove[] с обикновени и брейк движения. За брейк движенията отпечатай и сложността. За нея ти трябва променливата от проверката.
moonwalk: DanceMovewindmill: BreakDanceMove, сложност 8салса: DanceMove3. Автопарк. Вземи 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 € фактури.
Тест