В настоящата глава ще се запознаем с методи и ще научим какво представляват те, както и кои са базовите концепции при работа с тях. Ще научим защо е добра практика да ги използваме, как да ги декларираме и извикваме. Ще се запознаем с параметри и връщана стойност на метод, както и как да използваме тази връщана стойност. Накрая на главата, ще разгледаме утвърдените практики при използване на методите.
До момента установихме, че при писане на код на програма, която решава дадена задача, ни улеснява това, че разделяме задачата на части. Всяка част отговаря за дадено действие и по този начин не само ни е по-лесно да решим задачата, но и значително се подобрява както четимостта на кода, така и проследяването за грешки.
Всяко едно парче код, което изпълнява дадена функционалност и което сме отделили логически, може да изземе функционалността на метода. Точно това представляват методите – парчета код, които са именувани от нас по определен начин и които могат да бъдат извикани толкова пъти, колкото имаме нужда.
Един метод може да бъде извикан толкова пъти, колкото ние преценим, че ни е нужно за решаване на даден проблем. Това ни спестява повторението на един и същи код няколко пъти, както и намалява възможността да пропуснем грешка при евентуална корекция на въпросния код.
Простите методи отговарят за изпълнението на дадено действие, което спомага за решаване на определен проблем. Такива действия могат да бъдат разпечатване на даден низ на конзолата, извършване на някаква проверка, изпълнение на цикъл и други.
Нека разгледаме следния пример за прост метод:
Този метод има задачата да отпечата заглавие, което представлява поредица от символа -
. Поради тази причина името му е 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 системата: 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
метода.
Тествайте решението си тук: 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
, и го имплементираме.
От задачите за рисуване на конзолата си спомняме, че е добра практика да разделяме фигурата на няколко части. За наше улеснение ще разделим триъгълника на три части - горна, средна линия и долна.
Следващата ни стъпка е с цикъл да разпечатаме горната половина от триъгълника:
След това разпечатваме средната линия:
Накрая разпечатваме долната част от триъгълника, като този път стъпката на цикъла намалява.
Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/594#2.
Да се нарисува на конзолата запълнен квадрат със страна n, както е показно в примерите.
Вход | Изход | Вход | Изход |
---|---|---|---|
4 | -------- -\/\/\/- -\/\/\/- -------- |
5 | ---------- -\/\/\/\/- -\/\/\/\/- -\/\/\/\/- ---------- |
Първата ни стъпка е да прочетем входа от конзолата. След това трябва да създадем метод, който ще принтира първия и последен ред, тъй като те са еднакви. Нека не забравяме, че трябва да му дадем описателно име и да му зададем като параметър дължината на страната. Ще използваме конструктора new string
.
Следващата ни стъпка е да създадем метод, който ще рисува на конзолата средните редове. Отново задаваме описателно име, например PrintMiddleRow
.
Накрая извикваме създадените методи в главния метод Main()
на програмата, за да нарисуваме целия квадрат:
Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/594#3.
До момента разгледахме методи, които извършват дадено действие, например отпечатване на даден текст, число или фигура на конзолата. Освен този тип методи, съществуват и такива, които могат да връщат някакъв резултат. Именно тези методи ще разгледаме в следващите редове.
До сега разглеждахме примери, в които при декларация на методи използвахме ключовата дума void
, която указва, че методът не връща резултат, а изпълнява определено действие.
Ако заместим void
с тип на променлива, то това ще укаже на програмата, че метода трябва да върне някаква стойност от указания тип. Тази върната стойност може да бъде от всякакъв тип – int
, string
, double
и т.н.
За да върне един метод резултат е нужно да внимаваме да напишем очаквания тип на резултата при декларацията на метода на мястото на void . |
Важно е да отбележим, че резултатът, който се връща от метода, може да е от тип, съвместим с типа на връщаната стойност на метода. Например, ако декларираният тип на връщаната стойност е double
, то можем да върнем резултат от тип int
.
За да получим резултат от метода, на помощ идва операторът return
. Той трябва да бъде използван в тялото на метода и указва на програмата да спре изпълнението му и да върне на извиквача на метода определена стойност, която се определя от израза след въпросния оператор return
.
В примера по-долу имаме метод, който чете две имена от конзолата, съединява ги и ги връща като резултат. Връщаната стойност е от тип string
:
Операторът return
може да бъде използван и във void
методи. Тогава самият метод ще спре изпълнението си, без да връща никаква стойност, а след него не трябва да има израз, който да бъде върнат. В този случай употребата на return
е единствено за излизане от метода.
Има случаи, в които return
може да бъде извикван от няколко места в метода, но само ако има определени входни условия.
В примера по-долу имаме метод, който сравнява две числа и връща резултат съответно -1
, 0
или 1
според това дали първият аргумент е по-малък, равен или по-голям от втория аргумент, подаден на функцията. Методът използва ключо-вата дума return
на три различни места, за да върне три различни стойности според логиката на сравненията на числата:
След return
оператора, в текущия блок, не трябва да има други редове код, тъй като тогава Visual Studio ще покаже предупреждение, съобщавайки ни, че е засякъл код, който не може да бъде достъпен:
След като даден метод е изпълнен и върне стойност, то тази стойност може да се използва по няколко начина.
Първият е да присвоим резултата като стойност на променлива от съвместим тип:
Вторият е резултатът да бъде използван в израз:
Третият е да подадем резултата от работата на метода към друг метод:
Да се напише метод, който изчислява лицето на триъгълник по дадени основа и височина и връща стойността му.
Вход | Изход |
---|---|
3 4 |
6 |
Първата ни стъпка е да прочетем входа. След това създаваме метод, но този път внимаваме при декларацията да подадем коректния тип данни, които искаме метода да върне, а именно double
.
Следващата ни стъпка е да извикаме новия метод от нашия Main()
метод и да запишем върнатата стойност в подходяща променлива.
Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/594#4.
Да се напише метод, който изчислява и връща резултата от повдигането на число на дадена степен.
Вход | Изход | Вход | Изход |
---|---|---|---|
2 8 |
256 | 3 4 |
81 |
Първата ни стъпка отново ще е да прочетем входните данни от конзолата. Следващата стъпка е да създадем метод, който ще приема два параметъра (числото и степента) и ще връща като резултат число от тип double
.
След като сме направили нужните изчисления, ни остава да разпечатаме резултата в главния метод Main()
на програмата.
Тествайте решението си тук: 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
:
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).
Както споменахме, ако използваме едно и също име за няколко метода с различни сигнатури, то това означава, че имаме варианти на метод. Кодът по-долу показва как три различни метода могат да са с едно и също име, но да имат различни сигнатури и да изпълняват различни действия.
Важно е да отбележим, че връщаният тип като резултат на метода не е част от сигнатурата му. Ако връщаната стойност беше част от сигнатурата на метода, то няма как компилаторът да знае кой метод точно да извика.
Нека разгледаме следния пример - имаме два метода с различен тип на връщаната стойност. Въпреки това 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()
.
Тествайте решението си тук: 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, <name>!".
Вход | Изход |
---|---|
Peter | Hello, Peter! |
Дефинирайте метод PrintName(string name)
и го имплементирайте, след което в главната програма прочетете от конзолата име на човек и извикайте метода като му подадете прочетеното име.
Тествайте решението си тук: 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);
Тествайте решението си тук: 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.
Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/594#9.
Да се напише метод FindNthDigit(number, index)
, който получава число и индекс N като параметри и печата N-тата цифра на числото (като се брои от дясно на ляво, започвайки от 1). След това, резултатът да се отпечата на конзолата.
Вход | Изход | Вход | Изход | Вход | Изход |
---|---|---|---|---|---|
83746 2 |
4 | 93847837 6 |
8 | 2435 4 |
2 |
За да изпълним алгоритъма, ще използваме while
цикъл, докато дадено число не стане 0. На всяка итерация на while
цикъла ще проверяваме дали настоящият индекс на цифрата не отговаря на индекса, който търсим. Ако отговаря, ще върнем като резултат цифрата на индекса (number % 10
). Ако не отговаря, ще премахнем последната цифра на числото (number = number / 10
). Трябва да следим коя цифра проверяваме по индекс (от дясно на ляво, започвайки от 1). Когато намерим цифрата, ще върнем индекса.
Тествайте решението си тук: 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;
}
Тествайте решението си тук: 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()
прочетете типа съобщение от конзолата и според прочетения тип прочетете останалите данни (още един два или три реда). След това извикайте съответния метод за печатане на съответния тип съобщение.
Тествайте решението си тук: 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" и т.н.
Тествайте решението си тук: 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-та цифра", а за да създадем низа, ще процедираме както в задачата "Число към бройна система".
Тествайте решението си тук: https://judge.softuni.org/Contests/Practice/Index/594#14.