Поведение
Проблем
Всеки тегли по своему
Банката има клас Account със собственик и баланс. Парите се теглят на две места в програмата. Банкоматът проверява дали има достатъчно пари. Плащането с карта е писано от друг човек и не проверява:
Account account = new Account("Мария", 100);
// Банкоматът проверява дали има достатъчно пари.if (account.Balance >= 70){ account.Balance -= 70;}
// Плащането с карта е писано от друг човек и проверка няма.account.Balance -= 50;
Console.WriteLine(account.Balance);-20Сметката е на минус. Правилото „не можеш да изтеглиш повече, отколкото имаш“ е важно, а е написано (или забравено) поотделно на всяко място, където се тегли.
Идеята
Методите живеят при данните
Логиката за теглене е част от сметката, затова я сложи в класа Account. Метод в класа, без static, е метод на обекта:
class Account{ public string Owner; public double Balance;
public Account(string owner, double balance) { Owner = owner; Balance = balance; }
public void Deposit(double amount) { Balance += amount; }
public void Withdraw(double amount) { if (amount <= Balance) { Balance -= amount; } else { Console.WriteLine("Няма достатъчно пари: " + Owner); } }}
class Program{ static void Main() { Account account = new Account("Мария", 100); account.Withdraw(70); account.Withdraw(50); account.Deposit(20); Console.WriteLine(account.Balance); }}Няма достатъчно пари: Мария50- Методът се вика през обект и точка:
account.Withdraw(70), както полетата. - Вътре
Balanceе балансът на тази сметка, на обекта пред точката. Може да се пише иthis.Balance. - Проверката е написана веднъж. Който тегли през метода, минава през нея.
Account maria = new Account("Мария", 100);Account peter = new Account("Петър", 40);maria.Withdraw(70);peter.Withdraw(70);Console.WriteLine(maria.Balance + " " + peter.Balance);Стъпка по стъпка, с думи
- Две сметки, два обекта. Всеки има свой баланс.
- maria.Withdraw(70): в this отива стрелка към обекта пред точката.
- В метода балансът е на this: 100 - 70 = 30.
- Същият метод, друг обект: сега this сочи Петър.
- 70 е повече от 40: методът печата отказ и балансът на Петър не се пипа.
- Кодът на метода е един. Полетата са на обекта, през който го викаш.
Плащането с карта вече не може да „забрави“ проверката, ако тегли с Withdraw. Може обаче още да пише направо в полето. Как да го спрем, ще видим в урока „Капсулация“.
Идеята
Метод на обекта или static метод
Методите от модул „Методи“ бяха static. Те получават всичко като параметри и нямат свой обект. Методът на обекта работи с полетата на обекта, през който е извикан:
static double ApplyDiscount(double price, double percent){ return price * (100 - percent) / 100;}
double price = ApplyDiscount(10, 10);account.Withdraw(70);Метод на обекта не можеш да извикаш през името на класа. Не е ясно от чия сметка да тегли:
Account.Withdraw(70);error CS0120: An object reference is required for the non-static field, method, or property 'Account.Withdraw(double)'Какво точно значи static и кога ти трябва, ще видим в урока „static: общото за всички“.
Пробвай
Чия сметка?
Какво ще отпечата програмата?
Account maria = new Account("Мария", 100);Account peter = new Account("Петър", 40);maria.Deposit(30);peter.Withdraw(10);Console.WriteLine(maria.Balance + " " + peter.Balance);Всеки обект има свой баланс. maria.Deposit(30) сменя само баланса на Мария, а peter.Withdraw(10) само на Петър. Петър има 40, затова тегленето на 10 минава.
Пробвай
Намери грешката
Избери реда с грешката.
static пред метода значи, че той няма обект. Тогава няма и чий баланс да види. Компилаторът спира на реда с баланса:
error CS0120: An object reference is required for the non-static field, method, or property 'Account.Balance'Но грешката е в заглавието на метода: махни static.
Идеята
Методът може и да отговаря
Метод на обекта може да връща резултат, както методите от модул „Методи“. Продуктът знае цената и бройката си, затова може сам да каже колко струва:
class Product{ // полетата и конструкторът, както преди
public double Total() { return Price * Quantity; }}
Product[] cart ={ new Product("Хляб", 1.20, 2), new Product("Мляко", 2.10, 1), new Product("Кафе", 7.50, 1)};double sum = 0;foreach (Product p in cart){ sum += p.Total();}Console.WriteLine("Общо: " + sum + " €");Общо: 12 €Сравни с първия урок от модула: там сумата се смяташе в Main от полетата. Сега Main само пита всеки продукт. Ако утре се добави отстъпка за количество, променяш само Total().
Идеята
Как изглежда обектът като текст?
Отпечаташ ли продукт направо, излиза нещо безполезно:
Product coffee = new Product("Кафе", 7.50, 1);Console.WriteLine(coffee);ProductC# печата само името на класа.
Класът може да каже как да се показва. Методът се казва ToString() и връща текст:
class Product{ // полетата и конструкторът, както преди
public double Total() { return Price * Quantity; }
public override string ToString() { return Name + " x" + Quantity + ": " + Total() + " €"; }}
Product coffee = new Product("Кафе", 7.50, 1);Product bread = new Product("Хляб", 1.20, 2);Console.WriteLine(coffee);Console.WriteLine("В количката: " + bread);Кафе x1: 7.5 €В количката: Хляб x2: 2.4 €Не го викаш сам. Console.WriteLine(coffee) и "В количката: " + bread го викат вместо теб, когато трябва обектът да стане текст.
override в заглавието засега пиши точно така. Какво значи, ще разберем в модул „Наследяване и полиморфизъм“.
Пробвай
Книга в изречение
Какво ще отпечата програмата?
class Book{ public string Title; public string Author;
public Book(string title, string author) { Title = title; Author = author; }
public override string ToString() { return Title + " от " + Author; }}
Book book = new Book("Тютюн", "Димитър Димов");Console.WriteLine("Чета: " + book);"Чета: " + book трябва да направи book на текст. Затова се вика ToString() на книгата, а тя връща заглавието и автора.Упражнение
Обекти, които правят нещо
Реши задачите в редактора си. Вземи класовете от миналите уроци и им добави методи.
Person: методIntroduce(), който печата името и възрастта.SimpleCar: методAccelerate(), който увеличава скоростта с 10. Сега Main не пипа скоростта направо.DanceMove: методPerform(), който „изпълнява“ стъпката.Book: методRead(), който печата „Прочитане на [заглавие] от [автор]“.
Създай по един обект от всеки клас и извикай методите му:
Здравей, аз съм Мария и съм на 34 г.Скорост след ускоряване: 10Изпълнявам moonwalk за 5 секундиПрочитане на Под игото от Иван ВазовУпражнение
Кола с гориво
Клас Car с марка, скорост, гориво (в литри) и изминати километри. Конструкторът получава марката и горивото, скоростта и километрите започват от 0.
Accelerate()увеличава скоростта с 10.Refuel(double liters)долива гориво, но резервоарът побира най-много 50 литра.Drive(int distance)харчи по 6 литра на 100 км и добавя разстоянието към километрите. Ако горивото не стига, печата съобщение и колата не тръгва.ToString()връща марката, километрите и горивото.
Car car = new Car("Toyota", 20);car.Accelerate();car.Accelerate();Console.WriteLine("Скорост: " + car.Speed);car.Drive(100);Console.WriteLine(car);car.Drive(300);car.Refuel(40);Console.WriteLine(car);car.Drive(300);Console.WriteLine(car);Скорост: 20Toyota: 100 км, 14 лНяма достатъчно гориво за 300 кмToyota: 100 км, 50 лToyota: 400 км, 32 лdistance * 6 / 100.0. С 100 вместо 100.0 делението е целочислено и за 30 км ще излезе 1 литър вместо 1,8.Още задачи
- Отстъпка. Добави към
ProductметодApplyDiscount(double percent), който намалява цената на продукта. Сравни го със static метода от модул „Методи“, който връщаше нова цена. - Сметка с история. Добави към
Accountброяч на операциите: всяко внасяне и успешно теглене го увеличава с 1. МетодToString()връща собственика, баланса и броя на операциите.
Тест