Skip to content

Latest commit

 

History

History
792 lines (447 loc) · 75 KB

chapter-10-methods.md

File metadata and controls

792 lines (447 loc) · 75 KB

Глава 10. Методи

В настоящата глава ще се запознаем с методи и ще научим какво представляват те, както и кои са базовите концепции при работа с тях. Ще научим защо е добра практика да ги използваме, как да ги декларираме и извикваме. Ще се запознаем с параметри и връщана стойност на метод, както и как да използваме тази връщана стойност. Накрая на главата, ще разгледаме утвърдените практики при използване на методите.

Какво е "метод"?

До момента установихме, че при писане на код на програма, която решава дадена задача, ни улеснява това, че разделяме задачата на части. Всяка част отговаря за дадено действие и по този начин не само ни е по-лесно да решим задачата, но и значително се подобрява както четимостта на кода, така и проследяването за грешки.

Всяко едно парче код, което изпълнява дадена функционалност и което сме отделили логически, може да изземе функционалността на метода. Точно това представляват методите – парчета код, които са именувани от нас по определен начин и които могат да бъдат извикани толкова пъти, колкото имаме нужда.

Един метод може да бъде извикан толкова пъти, колкото ние преценим, че ни е нужно за решаване на даден проблем. Това ни спестява повторението на един и същи код няколко пъти, както и намалява възможността да пропуснем грешка при евентуална корекция на въпросния код.

Прости методи

Простите методи отговарят за изпълнението на дадено действие, което спомага за решаване на определен проблем. Такива действия могат да бъдат разпечатване на даден низ на конзолата, извършване на някаква проверка, изпълнение на цикъл и други.

Нека разгледаме следния пример за прост метод:

Този метод има задачата да отпечата заглавие, което представлява поредица от символа -. Поради тази причина името му е PrintHeader. Кръглите скоби ( и ) винаги следват името, независимо как сме именували метода. По-късно ще разгледаме как трябва да именуваме методите, с които работим, а за момента ще отбележим само, че е важно името му да описва действието, което той извършва.

Тялото на метода съдържа програмния код, който се намира между къдравите скоби { и }. Тези скоби винаги следват декларацията му и между тях поставяме кода, който решава проблема, описан от името на метода.

Защо да използваме методи?

До тук установихме, че методите спомагат за разделянето на обемна задача на по-малки части, което води до по-лесно решаване на въпросното задание. Това прави програмата ни не само по-добре структурирана и лесно четима, но и по-разбираема.

Чрез методите избягваме повторението на програмен код. Повтарящият се код е лоша практика, тъй като силно затруднява поддръжката на програмата и води до грешки. Ако дадена част от кода ни присъства в програмата няколко пъти и се наложи да променим нещо, то промените трябва да бъдат направени във всяко едно повторение на въпросния код. Вероятността да пропуснем място, на което трябва да нанесем корекция, е много голяма, което би довело до некоректно поведение на програмата. Това е причината, поради която е добра практика, ако използваме даден фрагмент код повече от веднъж в програмата си, да го дефинираме като отделен метод.

Методите ни предоставят възможността да използваме даден код няколко пъти. С решаването на все повече и повече задачи ще установите, че използването на вече съществуващи методи спестява много време и усилия.

Деклариране на методи

В езика C# декларираме методите в рамките на даден клас, т.е. между отварящата { и затваряща } скоби на класа. Декларирането представлява регистрирането на метода в програмата, за да бъде разпознаван в останалата част от нея. Най-добре познатият ни пример за метод е метода Main(…), който използваме във всяка една програма, която пишем.

Със следващия пример ще разгледаме задължителните елементи в декларацията на един метод.

  • Тип на връщаната стойност. В случая типа е double, което означава, че методът от примера ще върне резултат, който е от тип double. Връщаната стойност може да бъде както int, double, string и т.н., така и void. Ако типът е void, то това означава, че методът не връща резултат, а само изпълнява дадена операция.
  • Име на метода. Името на метода е определено от нас, като не забравяме, че трябва да описва функцията, която е изпълнявана от кода в тялото му. В примера името е GetSquare, което ни указва, че задачата на този метод е да изчисли лицето на квадрат.
  • Списък с параметри. Декларира се между скобите ( и ), които изписваме след името му. Тук изброяваме поредицата от параметри, които метода ще използва. Може да присъства само един параметър, няколко такива или да е празен списък. Ако няма параметри, то ще запишем само скобите (). В конкретния пример декларираме параметъра double num.
  • Декларация static в описанието на метода. За момента може да приемем, че static се пише винаги, когато се декларира метод, а по-късно, когато се запознаем с обектно-ориентираното програмиране (ООП), ще разберем разликата между статични методи (споделени за целия клас) и методи на обект, които работят върху данните на конкретна инстанция на класа (обект).

При деклариране на методи е важно да спазваме последователността на основните му елементи - първо тип на връщаната стойност, след това име на метода и накрая списък от параметри, ограден с кръгли скоби ().

След като сме декларирали метода, следва неговата имплементация (тяло). В тялото на метода описваме алгоритъма, по който той решава даден проблем, т.е. тялото съдържа кода (програмен блок), който реализира логиката на метода. В показания пример изчисляваме лицето на квадрат, а именно num * num.

Когато декларираме дадена променлива в тялото на един метод, я наричаме локална променлива за метода. Областта, в която съществува и може да бъде използвана тази променлива, започва от реда, на който сме я декларирали и стига до затварящата къдрава скоба } на тялото на метода. Тази област се нарича област на видимост на променливата (variable scope).

Извикване на методи

Извикването на метод представлява стартирането на изпълнението на кода, който се намира в тялото на метода. Това става като изпишем името му, последвано от кръглите скоби () и знака ; за край на реда. Ако методът ни изисква входни данни, то те се подават в скобите (), като последователността на фактическите параметри трябва да съвпада с последователността на подадените при декларирането на метода. Ето един пример:

Даден метод може да бъде извикан от няколко места в нашата програма. Единият начин е да бъде извикан от главния метод.

Метод може да бъде извикан и от тялото на друг метод, който не е главния метод на програмата ни.

Съществува вариант методът да бъде извикан от собственото си тяло. Това се нарича рекурсия и можете да намерите повече информация за нея в Wikipedia или да потърсите сами в Интернет.

Важно е да знаем, че ако един метод е деклариран в даден клас, то той може да бъде извикван преди реда, на който е деклариран.

Пример: празна касова бележка

Да се напише метод, който печата празна касова бележка. Методът трябва да извиква други три метода: един за принтиране на заглавието, един за основната част на бележката и един за долната част.

Част от касовата бележка Текст
Горна част CASH RECEIPT
------------------------------
Средна част Charged to____________________
Received by___________________
Долна част ------------------------------
(c) SoftUni

Примерен вход и изход

Вход Изход
(няма) CASH RECEIPT
------------------------------
Charged to____________________
Received by___________________
------------------------------
(c) SoftUni

Насоки и подсказки

Първата ни стъпка е да създадем void метод за принтиране на заглавната част от касовата бележка (header). Нека му дадем смислено име, което описва кратко и ясно задачата му, например PrintReceiptHeader. В тялото му ще напишем кода от примера по-долу:

Съвсем аналогично ще създадем още два метода за разпечатване на средната част на бележката (тяло) PrintReceiptBody и за разпечатване на долната част на бележката (footer) PrintReceiptFooter.

След това ще създадем и още един метод, който ще извиква трите метода, които написахме до момента един след друг:

Накрая ще извикаме метода PrintReceipt от тялото на главния Main метод за нашата програма:

Тестване в Judge системата

Програмата с общо пет метода, които се извикват един от друг, е готова и можем да я изпълним и тестваме, след което да я пратим за проверка в judge системата: https://judge.softuni.org/Contests/Practice/Index/594#0.

Методи с параметри

Много често в практиката, за да бъде решен даден проблем, методът, с чиято помощ постигаме това, се нуждае от допълнителна информация, която зависи от задачата му. Именно тази информация представляват параметрите на метода и неговото поведение зависи от тях.

Използване на параметри в методите

Както отбелязахме по-горе, параметрите освен нула на брой, могат също така да са един или няколко. При декларацията им ги разделяме със запетая. Те могат да бъдат от всеки един тип (int, string и т.н.), а по-долу е показан пример как точно ще бъдат използвани от метода.

Декларираме метода и списъка му с параметри, след което пишем кода, който той ще изпълнява.

След това извикваме метода и му подаваме конкретни стойности:

При декларирането на параметри можем да използваме различни типове променливи, като трябва да внимаване всеки един параметър да има тип и име. Важно е да отбележим, че при последващото извикване на метода, трябва да подаваме стойности за параметрите по реда, в който са декларирани самите те. Ако имаме подадени параметри в реда int и след това string, при извикването му не можем да подадем първо стойност за string и след това за int. Единствено можем да разменяме местата на подадените параметри, ако изрично изпишем преди това името на параметъра, както ще забележим малко по-нататък в един от примерите. Това като цяло не е добра практика!

Нека разгледаме примера за декларация на метод, който има няколко параметъра от различен тип.

Пример: знак на цяло число

Да се създаде метод, който печата знака на цяло число n.

Примерен вход и изход

Вход Изход
2 The number 2 is positive.
-5 The number -5 is negative.
0 The number 0 is zero.

Насоки и подсказки

Първата ни стъпка е създаването на метод и даването му на описателно име, например PrintSign. Този метод ще има само един параметър от тип int.

Следващата ни стъпка е имплементирането на логиката, по която програмата ни ще проверява какъв точно е знакът на числото. От примерите виждаме, че има три случая - числото е по-голямо от нула, равно на нула или по-малко от нула, което означава, че ще направим три проверки в тялото на метода.

Следващата ни стъпка е да прочетем входното число и да извикаме новия метод от тялото на Main метода.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/594#1.

Незадължителни параметри

Езикът C# поддържа използването на незадължителни параметри. Те позволяват пропускането на параметри при извикването на метода. Декларирането им става чрез осигуряване на стойност по подразбиране в описанието на съответния параметър.

Следващият пример онагледява употребата на незадължителните параметри:

Показаният метод PrintNumbers може да бъде извикан по няколко начина:

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

Да се създаде метод, който принтира триъгълник, както е показано в примерите.

Примерен вход и изход

Вход Изход Вход Изход
3 1
1 2
1 2 3
1 2
1
4 1
1 2
1 2 3
1 2 3 4
1 2 3
1 2
1

Насоки и подсказки

Преди да създадем метод за принтиране на един ред с дадени начало и край, прочитаме входното число от конзолата. След това избираме смислено име за метода, което описва целта му, например PrintLine, и го имплементираме.

От задачите за рисуване на конзолата си спомняме, че е добра практика да разделяме фигурата на няколко части. За наше улеснение ще разделим триъгълника на три части - горна, средна линия и долна.

Следващата ни стъпка е с цикъл да разпечатаме горната половина от триъгълника:

След това разпечатваме средната линия:

Накрая разпечатваме долната част от триъгълника, като този път стъпката на цикъла намалява.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/594#2.

Пример: рисуване на запълнен квадрат

Да се нарисува на конзолата запълнен квадрат със страна n, както е показно в примерите.

Примерен вход и изход

Вход Изход Вход Изход
4 --------
-\/\/\/-
-\/\/\/-
--------
5 ----------
-\/\/\/\/-
-\/\/\/\/-
-\/\/\/\/-
----------

Насоки и подсказки

Първата ни стъпка е да прочетем входа от конзолата. След това трябва да създадем метод, който ще принтира първия и последен ред, тъй като те са еднакви. Нека не забравяме, че трябва да му дадем описателно име и да му зададем като параметър дължината на страната. Ще използваме конструктора new string.

Следващата ни стъпка е да създадем метод, който ще рисува на конзолата средните редове. Отново задаваме описателно име, например PrintMiddleRow.

Накрая извикваме създадените методи в главния метод Main() на програмата, за да нарисуваме целия квадрат:

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/594#3.

Връщане на резултат от метод

До момента разгледахме методи, които извършват дадено действие, например отпечатване на даден текст, число или фигура на конзолата. Освен този тип методи, съществуват и такива, които могат да връщат някакъв резултат. Именно тези методи ще разгледаме в следващите редове.

Типове на връщаната от метода стойност

До сега разглеждахме примери, в които при декларация на методи използвахме ключовата дума void, която указва, че методът не връща резултат, а изпълнява определено действие.

Ако заместим void с тип на променлива, то това ще укаже на програмата, че метода трябва да върне някаква стойност от указания тип. Тази върната стойност може да бъде от всякакъв тип – int, string, double и т.н.

За да върне един метод резултат е нужно да внимаваме да напишем очаквания тип на резултата при декларацията на метода на мястото на void.

Важно е да отбележим, че резултатът, който се връща от метода, може да е от тип, съвместим с типа на връщаната стойност на метода. Например, ако декларираният тип на връщаната стойност е double, то можем да върнем резултат от тип int.

Оператор return

За да получим резултат от метода, на помощ идва операторът return. Той трябва да бъде използван в тялото на метода и указва на програмата да спре изпълнението му и да върне на извиквача на метода определена стойност, която се определя от израза след въпросния оператор return.

В примера по-долу имаме метод, който чете две имена от конзолата, съединява ги и ги връща като резултат. Връщаната стойност е от тип string:

Операторът return може да бъде използван и във void методи. Тогава самият метод ще спре изпълнението си, без да връща никаква стойност, а след него не трябва да има израз, който да бъде върнат. В този случай употребата на return е единствено за излизане от метода.

Има случаи, в които return може да бъде извикван от няколко места в метода, но само ако има определени входни условия.

В примера по-долу имаме метод, който сравнява две числа и връща резултат съответно -1, 0 или 1 според това дали първият аргумент е по-малък, равен или по-голям от втория аргумент, подаден на функцията. Методът използва ключо-вата дума return на три различни места, за да върне три различни стойности според логиката на сравненията на числата:

Кодът след return е недостъпен

След return оператора, в текущия блок, не трябва да има други редове код, тъй като тогава Visual Studio ще покаже предупреждение, съобщавайки ни, че е засякъл код, който не може да бъде достъпен:

В програмирането не може да има два пъти оператор return един след друг, защото изпълнението на първия няма да позволи да се изпълни вторият. Понякога програмистите се шегуват с фразата “пиши return; return; и да си ходим”, за да обяснят, че логиката на програмата е объркана.

Употреба на връщаната от метода стойност

След като даден метод е изпълнен и върне стойност, то тази стойност може да се използва по няколко начина.

Първият е да присвоим резултата като стойност на променлива от съвместим тип:

Вторият е резултатът да бъде използван в израз:

Третият е да подадем резултата от работата на метода към друг метод:

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

Да се напише метод, който изчислява лицето на триъгълник по дадени основа и височина и връща стойността му.

Примерен вход и изход

Вход Изход
3
4
6

Насоки и подсказки

Първата ни стъпка е да прочетем входа. След това създаваме метод, но този път внимаваме при декларацията да подадем коректния тип данни, които искаме метода да върне, а именно double.

Следващата ни стъпка е да извикаме новия метод от нашия Main() метод и да запишем върнатата стойност в подходяща променлива.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/594#4.

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

Да се напише метод, който изчислява и връща резултата от повдигането на число на дадена степен.

Примерен вход и изход

Вход Изход Вход Изход
2
8
256 3
4
81

Насоки и подсказки

Първата ни стъпка отново ще е да прочетем входните данни от конзолата. Следващата стъпка е да създадем метод, който ще приема два параметъра (числото и степента) и ще връща като резултат число от тип double.

След като сме направили нужните изчисления, ни остава да разпечатаме резултата в главния метод Main() на програмата.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/594#5.

Методи, връщащи няколко стойности

В практиката се срещат случаи, в които се нуждаем даден метод да върне повече от един елемент като резултат. За да е възможен подобен сценарий във Visual Studio и C# (от C# 7 нататък) е интегриран стойностният тип ValueTuple, както и литерал от тип ValueTuple. Накратко типът ValueTuple представлява съвкупност от две стойности, позволяващи временното съхранение на множество стойности. Стойностите биват съхранявани в променливи (полета - какво са полета, ще разгледаме на по-късен етап) от съответните типове. Въпреки, че типът Tuple съществува и преди C# 7, той не е добре поддържан от езика в предишните му версии и е неефективен. Затова в предходните версии на езика C# елементите в един Tuple са представяни като Item1, Item2 и т.н. и имената на техните променливи (променливите, в които се съхраняват) е било невъзможно да бъдат променяни. В C# 7 е въведена поддръжка на типа (ValueTuple), което позволява задаване на смислови имена на елементите в един ValueTuple.

Деклариране на ValueTuple

Нека разгледаме примерна декларация на променлива от тип ValueTuple:

var personInfo = (name: "Steeve", age: 27, "Bulgaria");

За улеснение при декларирането използваме ключовата дума var, а в скобите изброяваме имената на желаните стойности, следвани от самите стойности. Нека погледнем и в дебъг режим какво се съдържа в променливата personInfo:

Виждаме, че се състои от няколко полета с имена и стойности, описани при инициализацията на променливата. Забелязваме, че последната променлива е именувана Item3. Това е така, защото по време на инициализацията не сме уточнили име за променливата, в която се пази стойността "Bulgaria". В такъв случай именуването е по подразбиране, т.е. променливите са именувани с Item1, Item2, Item3 и т.н.

Метод, връщащ няколко стойности

Следният метод приема за параметри две целочислени числа (x и y) и връща две стойности - резултата от целочислено деление на двете числа и остатъка от делението им:

static (int result, int reminder) Divide(int x, int y)
{
    int result = x / y;
    int reminder = x % y;

    return (result, reminder);
}

Този метод връща резултат от тип ValueTuple, съдържащ две променливи (полета) от тип int, съответно именувани result и reminder. Извикването на метода се осъществява по следния начин:

var division = Divide(1, 3);

За да достъпим резултатите, върнати от метода, прилагаме точковата нотация към променливата division:

Варианти на методи

В много езици за програмиране един и същ метод може да е деклариран в няколко варианта с еднакво име и различни параметри. Това е известно с термина “method overloading”. Сега нека разгледаме как се пишат тези overloaded methods.

Сигнатура на метода

В програмирането начинът, по който се идентифицира един метод, е чрез двойката елементи от декларацията му – име на метода и списък от неговите параметри. Тези два елемента определят неговата спецификация, т. нар. сигнатура на метода.

Сигнатурата на метод се състои от името на метода и дефинициите на неговите параметри (подредба и тип на параметрите без значение на имената им). Пример:

В този пример сигнатурата на метода е неговото име (Print), заедно с типовете на неговите параметри (string).

Ако в програмата ни има методи с еднакви имена, но с различни сигнатури, то казваме, че имаме варианти на методи (method overloading).

Варианти на методи в езика C#

Както споменахме, ако използваме едно и също име за няколко метода с различни сигнатури, то това означава, че имаме варианти на метод. Кодът по-долу показва как три различни метода могат да са с едно и също име, но да имат различни сигнатури и да изпълняват различни действия.

Сигнатура и тип на връщаната стойност

Важно е да отбележим, че връщаният тип като резултат на метода не е част от сигнатурата му. Ако връщаната стойност беше част от сигнатурата на метода, то няма как компилаторът да знае кой метод точно да извика.

Нека разгледаме следния пример - имаме два метода с различен тип на връщаната стойност. Въпреки това Visual Studio ни показва, че има грешка, защото сигнатурите и на двата са еднакви. Съответно при опит за извикване на метод с име Print(…), компилаторът не би могъл да прецени кой от двата метода да изпълни.

Пример: по-голямата от две стойности

Като входни данни са дадени две стойности от един и същ тип. Стойностите могат да са от тип int, char или string. Да се създаде метод GetMax(), който връща като резултат по-голямата от двете стойности.

Примерен вход и изход

Вход Изход Вход Изход Вход Изход
int
2
16
16 char
a
z
z string
Ivan
Todor
Todor

Насоки и подсказки

За да създадем този метод, първо трябва да създадем три други метода с едно и също име и различни сигнатури. Първо създаваме метод, който ще сравнява цели числа.

Следвайки логиката от предходния метод, създаваме такъв със същото име, който обаче ще сравнява символи.

Следващият метод, който трябва да създадем, ще сравнява низове. Тук логиката ще е малко по-различна, тъй като стойностите от тип string не позволяват да бъдат сравнявани чрез операторите < и >. Ще използваме метода CompareTo(…), който връща числова стойност: по-голяма от 0 (сравняваният обект е по-голям), по-малка от 0 (сравняваният обект е по-малък) и 0 (при два еднакви обекта).

Последната стъпка е да прочетем входните данни, да използваме подходящи променливи и да извикаме метода GetMax() от тялото на метода Main().

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/594#6.

Вложени методи (локални функции)

Нека разгледаме следния пример:

Какво е локална функция?

Виждаме, че в този код, в главния метод Main() има друг деклариран метод Result(). Такъв вложен метод се нарича локална функция и е нововъведение в C# 7. Локалните функции могат да се декларират във всеки един друг метод. Когато C# компилаторът компилира такива функции, те биват превърнати в private методи. Тъй като разликата между public и private методи се изучава на по-късен етап, за момента ще отбележим, че private методите могат да се използват само в класа, в който са декларирани. Програмите, които пишем на това ниво, използват само един клас, затова и приемаме, че можем да използваме вложените методи без каквито и да било притеснения.

Защо да използваме локални функции?

С времето и практиката ще открием, че когато пишем код, често се нуждаем от методи, които бихме използвали само един път, или пък нужният ни метод става твърде дълъг. По-нагоре споменахме, че когато един метод съдържа в себе си прекалено много редове код, то той става труден за поддръжка и четене. В тези случаи на помощ идват локалните функции - те предоставят възможност в даден метод да се декларира друг метод, който ще бъде използван например само един път. Това спомага кода ни да е по-добре подреден и по-лесно четим, което от своя страна спомага за по-бърза корекция на евентуална грешка в кода и намалява възможността за грешки при промени в програмната логика.

Деклариране на локални функции

Нека отново разгледаме примера от по-горе.

В този пример, методът Result() е локална функция, тъй като е вложен в метода Main(), т.е. Result() е локален за Main(). Това означава, че методът Result() може да бъде използван само в метода Main(), тъй като е деклариран в него. Единствената разлика между вложените методи и обикновените методи е, че вложените методи не могат да бъдат static. Тъй като дефиницията за static метод се разглежда на по-късен етап, за момента ще приемем, че при декларирането на една локална функция, изписваме единствено типa на връщаната стойност, името на метода и списъка му с параметри. В конкретния разглеждан случай, това е double Result(double a, double b).

Локалните функции имат достъп до променливи, които се използват в съдържащия ги метод. Следващият пример демонстрира как се случва това.

Тази особеност на вложените методи ги прави много удобни помощници при решаването на дадена задача. Те спестяват време и код, които иначе бихме вложили, за да предаваме на вложените методи параметри и променливи, които се използват в методите, в които са вложени.

Именуване на методи. Добри практики при работа с методи

В тази част ще се запознаем с някои утвърдени практики при работа с методи, свързани с именуването, подредбата на кода и неговата структура.

Именуване на методи

Когато именуваме даден метод е препоръчително да използваме смислени имена. Тъй като всеки метод отговаря за някаква част от нашия проблем, то при именуването му трябва да вземем предвид действието, което той извършва, т.е. добра практика е името да описва неговата цел.

Задължително е името да започва с главна буква и трябва да е съставено от глагол или от двойка: глагол + съществително име. Форматирането на името става, спазвайки Upper Case Camel конвенцията, т.е. всяка дума, включително първата, започва с главна буква. Кръглите скоби ( и ) винаги следват името му.

Всеки метод трябва да изпълнява самостоятелна задача, а името на метода трябва да описва каква е неговата функция.

Няколко примера за коректно именуване на методи:

  • FindStudent
  • LoadReport
  • Sine

Няколко примера за лошо именуване на методи:

  • Method1
  • DoSomething
  • HandleStuff
  • SampleMethod
  • DirtyHack

Ако не можем да измислим подходящо име, то най-вероятно методът решава повече от една задача или няма ясно дефинирана цел и тогава трябва да помислим как да го разделим на няколко отделни метода.

Именуване на параметрите на методите

При именуването на параметрите на метода важат почти същите правила, както и при самите методи. Разликите тук са, че за имената на параметрите е добре да използваме съществително име или двойка от прилагателно и съществително име, както и че при именуване на параметрите се спазва lowerCamelCase конвенцията, т.е. всички думи без първата започват с главна буква. Трябва да отбележим, че е добра практика името на параметъра да указва каква е мерната единица, която се използва при работа с него.

Няколко примера за коректно именуване на параметри на методи:

  • firstName
  • report
  • speedKmH
  • usersList
  • fontSizeInPixels
  • font

Няколко примера за некоректно именуване на параметри:

  • p
  • p1
  • p2
  • populate
  • LastName
  • last_name

Добри практики при работа с методи

Нека отново припомним, че един метод трябва да изпълнява само една точно определена задача. Ако това не може да бъде постигнато, тогава трябва да помислим как да разделим метода на няколко отделни такива. Както казахме, името на метода трябва точно и ясно да описва неговата цел. Друга добра практика в програмирането е да избягваме методи, по-дълги от екрана ни (приблизително). Ако все пак кода стане много обемен, то е препоръчително метода да се раздели на няколко по-кратки, както в примера по-долу.

Структура и форматиране на кода

При писането на методи трябва да внимаваме да спазваме коректна индентация (отместване по-навътре на блокове от кода).

Пример за правилно форматиран C# код:

Пример за некоректно форматиран C# код:

Когато заглавният ред на метода е твърде дълъг, се препоръчва той да се раздели на няколко реда, като всеки ред след първия се отмества с две табулации надясно (за по-добра четимост):

Друга добра практика при писане на код е да оставяме празен ред между методите, след циклите и условните конструкции. Също така, опитвайте да избягвате да пишете дълги редове и сложни изрази. С времето ще установите, че това подобрява четимостта на кода и спестява време.

Препоръчваме винаги да се използват къдрави скоби за тялото на проверки и цикли. Скобите не само подобряват четимостта, но и намалят възможността да бъде допусната грешка и програмата ни да се държи некоректно.

Какво научихме от тази глава?

В тази глава се запознахме с базовите концепции при работа с методи:

  • Научихме, че целта на методите е да разделят големи програми с много редове код на по-малки и кратки задачи.
  • Запознахме се със структурата на методите, как да ги декларираме и извикваме по тяхното име.
  • Разгледахме примери за методи с параметри и как да ги използваме в нашата програма.
  • Научихме какво представляват сигнатурата и връщаната стойност на метода, както и каква е функцията на оператора return в методите.
  • Запознахме се с добрите практики при работа с методи, как да именуваме методите и техните параметри, как да форматираме кода и други.

Упражнения

За да затвърдим работата с методи, ще решим няколко задачи. В тях се изисква да напишете метод с определена функционалност и след това да го извикате като му подадете данни, прочетени от конзолата, точно както е показано в примерния вход и изход.

Задача: "Hello, Име!"

Да се напише метод, който получава като параметър име и принтира на конзолата "Hello, <name>!".

Примерен вход и изход

Вход Изход
Peter Hello, Peter!

Насоки и подсказки

Дефинирайте метод PrintName(string name) и го имплементирайте, след което в главната програма прочетете от конзолата име на човек и извикайте метода като му подадете прочетеното име.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/594#7.

Задача: по-малко число

Да се създаде метод GetMin(int a, int b), който връща по-малкото от две числа. Да се напише програма, която чете като входни данни от конзолата три числа и печата най-малкото от тях. Да се използва метода GetMin(…), който е вече създаден.

Примерен вход и изход

Вход Изход Вход Изход
1
2
3
1 -100
-101
-102
-102

Насоки и подсказки

Дефинирайте метод GetMin(int a, int b) и го имплементирайте, след което го извикайте от главната програма както е показано по-долу. За да намерите минимума на три числа, намерете първо минимума на първите две от тях и след това минимума на резултата и третото число:

var min = GetMin(GetMin(num1, num2), num3);

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/594#8.

Задача: повтаряне на низ

Да се напише метод RepeatString(str, count), който получава като параметри променлива от тип string и цяло число n и връща низа, повторен n пъти. След това резултатът да се отпечата на конзолата.

Примерен вход и изход

Вход Изход Вход Изход
str
2
strstr roki
6
rokirokirokirokirokiroki

Насоки и подсказки

Допишете метода по-долу като добавите съединяването входния низ към резултата в цикъла:

Имайте предвид, че в езика C# съединяването на низове в цикъл води до лоша производителност и не се препоръчва. Потърсете и пробвайте по-ефективни решения тук: https://stackoverflow.com/questions/411752.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/594#9.

Задача: n-та цифра

Да се напише метод FindNthDigit(number, index), който получава число и индекс N като параметри и печата N-тата цифра на числото (като се брои от дясно на ляво, започвайки от 1). След това, резултатът да се отпечата на конзолата.

Примерен вход и изход

Вход Изход Вход Изход Вход Изход
83746
2
4 93847837
6
8 2435
4
2

Насоки и подсказки

За да изпълним алгоритъма, ще използваме while цикъл, докато дадено число не стане 0. На всяка итерация на while цикъла ще проверяваме дали настоящият индекс на цифрата не отговаря на индекса, който търсим. Ако отговаря, ще върнем като резултат цифрата на индекса (number % 10). Ако не отговаря, ще премахнем последната цифра на числото (number = number / 10). Трябва да следим коя цифра проверяваме по индекс (от дясно на ляво, започвайки от 1). Когато намерим цифрата, ще върнем индекса.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/594#10.

Задача: число към бройна система

Да се напише метод IntegerToBase(number, toBase), който получава като параметри цяло число и основа на бройна система и връща входното число, конвертирано към посочената бройна система. След това, резултатът да се отпечата на конзолата. Входното число винаги ще е в бройна система 10, а параметърът за основа ще е между 2 и 10.

Примерен вход и изход

Вход Изход Вход Изход Вход Изход
3
2
11 4
4
10 9
7
12

Насоки и подсказки

За да решим задачата, ще декларираме стрингова променлива, в която ще пазим резултата. След това трябва да изпълним следните изчисления, нужни за конвертиране на числото.

  • Изчисляваме остатъка от числото, разделено на основата.
  • Вмъкваме остатъка от числото в началото на низа, представящ резултата.
  • Разделяме числото на основата.
  • Повтаряме алгоритъма, докато входното число не стане 0.

Допишете липсващата логика в метода по-долу:

static string IntegerToBase(int number, int toBase) {
    string result = "";
    while (number != 0) {
        // Implement the missing conversion logic
    }
    return result;
}

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/594#11.

Задача: известия

Да се напише програма, която прочита цяло число n и на следващите редове въвежда n съобщения (като за всяко съобщение се прочитат по няколко реда). Всяко съобщение започва с messageType: success, warning или error:

  • Когато messageType е success да се четат operation + message (всяко на отделен ред).
  • Когато messageType е warning да се чете само message.
  • Когато messageType е error да се четат operation + message + errorCode (всяко на отделен ред).

На конзолата да се отпечата всяко прочетено съобщение, форматирано в зависимост от неговия messageType. Като след заглавния ред за всяко съобщение да се отпечатат толкова на брой символа =, колкото е дълъг съответният заглавен ред и да се сложи по един празен ред след всяко съобщение (за по-детайлно разбиране погледнете примерите).

Задачата да се реши с дефиниране на четири метода: ShowSuccessMessage(), ShowWarningMessage(), ShowErrorMessage() и ReadAndProcessMessage(), като само последният метод да се извиква от главния Main() метод:

Примерен вход и изход

Вход Изход
4
error
credit card purchase
Invalid customer address
500
warning
Email not confirmed
success
user registration
User registered successfully
warning
Customer has not email assigned
Error: Failed to execute credit card purchase.
==============================================
Reason: Invalid customer address.
Error code: 500.

Warning: Email not confirmed.
=============================

Successfully executed user registration.
========================================
User registered successfully.

Warning: Customer has not email assigned.
=========================================

Насоки и подсказки

Дефинирайте и имплементирайте посочените четири метода.

В ReadAndProcessMessage() прочетете типа съобщение от конзолата и според прочетения тип прочетете останалите данни (още един два или три реда). След това извикайте съответния метод за печатане на съответния тип съобщение.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/594#12.

Задача: числа към думи

Да се напише метод Letterize(number), който прочита цяло число и го разпечатва с думи на английски език според условията по-долу:

  • Да се отпечатат с думи стотиците, десетиците и единиците (и евентуални минус) според правилата на английския език.
  • Ако числото е по-голямо от 999, трябва да се принтира "too large".
  • Ако числото е по-малко от -999, трябва да се принтира "too small".
  • Ако числото е отрицателно, трябва да се принтира "minus" преди него.
  • Ако числото не е съставено от три цифри, не трябва да се принтира.

Примерен вход и изход

Вход Изход Вход Изход
3
999
-420
1020
nine-hundred and ninety nine
minus four-hundred and twenty
too large
2
15
350
fifteen
three-hundred and fifty
Вход Изход Вход Изход
4
311
418
509
-9945
three-hundred and eleven
four-hundred and eighteen
five-hundred and nine
too small
3
500
123
9
five-hundred
one-hundred and twenty three
nine

Насоки и подсказки

Можем първо да отпечатаме стотиците като текст - (числото / 100) % 10, след тях десетиците - (числото / 10) % 10 и накрая единиците - (числото % 10).

Първият специален случай е когато числото е точно закръглено на 100 (напр. 100, 200, 300 и т.н.). В този случай отпечатваме "one-hundred", "two-hundred", "three-hundred" и т.н.

Вторият специален случай е когато числото, формирано от последните две цифри на входното число, е по-малко от 10 (напр. 101, 305, 609 и т.н.). В този случай отпечатваме "one-hundred and one", "three-hundred and five", "six-hundred and nine" и т.н.

Третият специален случай е когато числото, формирано от последните две цифри на входното число, е по-голямо от 10 и по-малко от 20 (напр. 111, 814, 919 и т.н.). В този случай отпечатваме "one-hundred and eleven", "eight-hundred and fourteen", "nine-hundred and nineteen" и т.н.

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/594#13.

Задача: криптиране на низ

Да се напише метод Encrypt(char letter), който криптира дадена буква по следния начин:

  • Вземат се първата и последна цифра от ASCII кода на буквата и се залепят една за друга в низ, който ще представя резултата.
  • Към началото на стойността на низа, който представя резултата, се залепя символа, който отговаря на следното условие:
    • ASCII кода на буквата + последната цифра от ASCII кода на буквата.
  • След това към края на стойността на низа, който представя резултата, се залепя символа, който отговаря на следното условие:
    • ASCII кода на буквата - първата цифра от ASCII кода на буквата.
  • Методът трябва да върне като резултат криптирания низ.

Пример:

  • j → p16i
    • ASCII кодът на j e 106 → Първа цифра - 1, последна цифра - 6.
    • Залепяме първата и последната цифра → 16.
    • Към началото на стойността на низа, който представя резултата, залепяме символа, който се получава от сбора на ASCII кода + последната цифра → 106 + 6 → 112 → p.
    • Към края на стойността на низа, който представя резултата, залепяме символа, който се получава от разликата на ASCII кода - първата цифра → 106 - 1 → 105 → i.

Използвайки метода, описан по-горе, да се напише програма, която чете поредица от символи, криптира ги и отпечатва резултата на един ред.

Приемаме, че входните данни винаги ще бъдат валидни. Главният метод трябва да прочита входните данни, подадени от потребителя – цяло число n, следвани от по един символ на всеки от следващите n реда.

Да се криптират символите и да се добавят към криптирания низ. Накрая като резултат трябва да се отпечата криптиран низ от символи като в следващия пример.

Пример:

  • S, o, f, t, U, n, i → V83Kp11nh12ez16sZ85Mn10mn15h

Примерен вход и изход

Вход Изход
7
S
o
f
t
U
n
i
V83Kp11nh12ez16sZ85Mn10mn15h
Вход Изход
7
B
i
r
a
H
a
x
H66<n15hv14qh97XJ72Ah97xx10w

Насоки и подсказки

На променливата от тип string, в която ще се пази стойността на резултата, ще присвоим първоначална стойност string.Empty. Трябва да се завърти цикъл n пъти, като на всяка итерация към променливата, в която пазим стойността на резултата, ще прибавяме криптирания символ.

За да намерим първата и последната цифри от ASCII кода, ще използваме алгоритъма, който използвахме за решаване на задача "N-та цифра", а за да създадем низа, ще процедираме както в задачата "Число към бройна система".

Тестване в Judge системата

Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/594#14.