Метод (програмиране)
- Вижте пояснителната страница за други значения на Метод.
Метод (на английски: method) в програмирането е съставна част от една програма, която решава конкретен проблем. Може както да приема параметри така и да връща стойност. Целта на писането на дадена програма е решаването на дадена задача. За да бъде ефективно решена дадена задача в програмирането, тя се разделя на подзадачи, разработват се алгоритми за решение на тези подзадачи и накрая тези подзадачи се сглобяват в цялостна програма. Обособените парчета код, решаващи дадената подзадача се наричат подпрограми (subroutines). В различните езици подпрограмите се срещат под други имена като функции (functions) или процедури (procedures). В C#, те се наричат методи (methods).
Най-простият пример за метод е "Main(…)", който винаги го декларираме между отварящата и затварящата скоба на нашия клас:
class HelloCSharp
{ // Отваряща скоба на класа
// Декларираме нашият метод между скобите на класа
static void Main(string[] args)
{
Console.WriteLine("Hello C#!");
}
} // Затваряща скоба на класа
Къде е позволено да декларираме метод
редактиранеВсеки клас (class) има отваряща и затваряща фигурна скоба – "{" и "}", между които се пише програмният код. Даден метод може да съществува само ако е деклариран между отварящата "{" и затварящата "}" скоба на даден клас. Допълнително изискване е методът да бъде деклариран извън имплементацията на друг метод. Следва по-сложен пример как се ползват методите и къде се декларират. Тази програма при въвеждане на две числа връща по-голямото, а кодът за намиране на по-голямото число е изведен в отделен метод (GetMaxCalculation):
using System;
internal class GetMax
{ // Отваряща скоба на класа
// Декларираме метода GetMaxCalculation и поставяме в него кода за намиране на по-голямото от две числа
internal static double GetMaxCalculation(double firstNumber, double secondNumber)
{
double theMaxNumber = firstNumber;
if (firstNumber < secondNumber)
{
theMaxNumber = secondNumber;
}
return theMaxNumber;
}
// Декларираме метода Main()
static void Main()
{
Console.Write("Enter number One: ");
double numberOne = double.Parse(Console.ReadLine());
Console.Write("Enter number Two: ");
double numberTwo = double.Parse(Console.ReadLine());
Console.WriteLine();
// Извикваме метода GetMaxCalculation и принтираме
Console.WriteLine("The Biggest Number is: {0}", GetMaxCalculation(numberOne, numberTwo));
Console.WriteLine();
}
} // Затваряща скоба на класа
Методите могат да бъдат декларирани с и без параметър. Горният пример е на метод, деклариран с два параметъра, т.е. internal static double GetMaxCalculation(double firstNumber, double secondNumber). Пример на метод без параметри е:
static int countNumber = 10;
internal static void PrintFirstOddNumbers()
{
for (int number = 0; number < countNumber; number++)
{
if (number / 2 == 0)
{
Console.WriteLine(number);
}
}
}
Извикване на метод от друг клас
редактиранеЗа да извикаме метод дефиниран в един клас от друг, то тогава той трябва да е или internal (достъпван само в съответното assembly) или public. Достъпът до метода става чрез името на класа. Private методи са видими само в съответния клас и не могат да бъдат достъпвани от друг.
using System;
class SimpleCalculations
{
double maxValue = GetMax.GetMaxCalculation(1.02, 3);
}
Ако искаме да разберем кой метод извиква нашият код може да използваме StackTrace(using System.Diagnostics;).
StackTrace stackTrace = new StackTrace();
var traceMethod = stackTrace.GetFrame(1).GetMethod().Name;
По този начин получаваме името на метода който извиква.
Основни правила за създаване и работа на един метод
редактиране- Всеки един метод трябва да решава само една точно определена задача. Обособяването като метод на парче код което решава няколко задачи (дори и свързани помежду си) води до затруднена четимост на програмата като цяло и до затруднено редактиране на кода.
- Името на метода трябва да е описателно и да отговаря на това, което изпълнява дадения метод. Ако името на метода се получи прекалено дълго това означава че този метод не решава само една задача. Например метод с име GetUserName() е пример за име което описва съвсем ясно какво прави той. Ако при писане на метода стигнете до име от типа на GetUserNameAndPasswordAndCheckInDataBaseForUserDetails() това трябва да ви покаже, че „натоварвате“ един метод с повече от една функция.
- Един метод трябва да „знае“ за „околната среда“ точно толкова колкото е необходимо за да може да си свърши работата, и трябва да бъде видим само от методи, които биха могли да имат работа с него. Предаването на масив от данни или структура към метод, който има нужда само от един елемент от тези данни е грешен подход и трябва да се избягва.
- Винаги когато в програмата нещо трябва да се извърши на повече от едно място то този код трябва да се обособи в отделен метод. По този начин евентуални бъдещи корекции ще трябва да се направят само в тялото на метода, а не на няколко места в основната програма, или в други методи.
Статични и динамични методи
редактиранеВ езика C# методите могат да бъдат „привързани“ както към даден клас като цяло (статични методи), така и към дадена инстанция (елемент) на дадения клас (динамични методи). Когато метода бъде дефиниран с ключовата дума static пред дефиницията си, то той е статичен. Този метод работи не върху конкретен обект от класа, а върху данни от целия клас.
Ако например имаме клас „автомобил“ и направим статичен метод за изчисляване на разхода на гориво, то този метод няма да се интересува за кой точно автомобил става въпрос. Той ще очаква да му се подадат като параметри пробега и изразходените литри гориво, и ще връща резултата в литри на 100 km. Така всички обекти, наследници на клас „автомобил“ ще могат да изчисляват разхода си, ползвайки този метод.
Можем да извикаме Automobile.GetAverageMPG(miles, gallons) без да му подаваме определен обект от клас „автомобил“. Както се забелязва от примера статичния метод се извиква като се укаже името на клас преди името на метода. Статичния метод може да се ползва след първо извикване на метод от дадения клас. Докато нямаме нито един обект от клас „автомобил“, нямаме и никакви методи от този клас и опит да извикаме метода ще доведе до грешка при изпълнението на програмата.
Динамичните методи за разлика от статичните са „вързани“ към точно определен обект на дадения клас, и извършват манипулации въз основа на данните на дадения обект, а не на класа като цяло.
Ако имаме два обекта — примерно carA и carB — от клас Automobiles, и всеки от обектите пази в себе си данни за последното показание на километража и заредените литри гориво, тогава извикването на carA.GetAverageMPG() и съответно на carB.GetAverageMPG() ще връща разхода на гориво за конкретния обект, ползвайки данните от обекта. Динамичните методи се извикват чрез добавяне на името на обекта преди името на метода.
Предефиниране на методи
редактиранеПредефинирането (Overloading) на метод е възможността да имаме методи с едно и също име, но с различен брой параметри, които да имат различен код в себе си. Това е възможно, тъй като компилатора различава един метод от друг не само по името, но и по броя (и типа) на параметрите предавани към него. Това ни позволява например да имаме процедура CalculateArea(), която да може да изчислява площта както на правоъгълник, така и на кръг (а по желание и на още геометрични фигури), стига броя и/или типа на подаваните параметри да е уникален. Важно: това не важи за имената на променливите! Параметри с различни имена които са от еднакъв тип не правят двата метода различни и компилатора няма да разреши такава дефиниция. Метод с име CalculateArea(int height, int width) може да се ползва за изчисляване на площта на правоъгълник, а метод със същото име, но само с един параметър: CalculateArea(doube radius) да изчислява площта на кръг. Дори и ако решим да ползваме същото име за процедура за изчисляване площта на квадрат по този начин: CalculateArea(int side) то това ще е правилно от гледна точка на компилатора, макар че има 2 метода с по 1 параметър. Разликата е че единия път параметъра е double, а другия int. В последния случай трябва да се подхожда с повишено внимание, тъй като ако имаме окръжност с радиус примерно 2, запомнен в int променливата radius извикването на метода ще изчисли площта за квадрат, а не за кръг. Но е достатъчно да се извика с area = CalculateArea((double) radius) за да се извика коректния метод за кръг.
Overriding на методи
редактиранеOverriding може да се приложи при унаследени методи, събития които са или abstract, или virtual. Използва се за да разшири или промени функционалността на метода.
public abstract class Shape
{
public abstract double Width { get; set; }
public abstract double Heigth { get; set; }
public abstract double CalculateSurface();
}
class Triangle: Shape
{
public override double Width { get; set; }
public override double Heigth { get; set; }
public override double CalculateSurface()
{
return (this.Width * this.Heigth)/2;
}
}
Може да се използва както за наши собствени методи, така и за методи които са стандартни за .NET като пример toString()
public override string ToString()
{
string student = string.Format("{0} | Grade: {1}", base.ToString(), this.Grade);
return student;
}
Специални методи
редактиранеСпециалните методи са специфични за отделните езици за програмиране, като някои поддържат всички изброени по долу методи, други само част, а някои езици не използват нито един от специалните методи. Обикновено компилатора на езика генерира автоматично специалните методи, но в някои езици, като C++, Java, C# и други, програмистът има възможност да дефинира специални методи. Обикновено компилаторът извиква автоматично специалните методи на подходящите места в програмата, но някои езици позволяват и извикването на специални методи в кода на програмата.
Конструктори
редактиранеКонструкторите са специални методи, които се извикват автоматично при създаването на обект от даден клас. Конструкторите могат да имат параметри, но обикновено не връщат стойност. Един клас може да има повече от един конструктор, като всеки да е с различен набор параметри. Тяхната задача е да инициализират обекта който се създава, което може да включва:
- задаване на стойности по подразбиране на членове на класа
- заделяне на ресурси
- присвояване на специфични стойности в зависимост от подадените параметри
Конструкторите на един клас в повечето езици за програмиране имат същото име като името на класа. Компилаторът автоматично създава конструктор за даден клас дори и да няма дефиниран от потребителя такъв.
Пример за дефиниран от програмиста конструктор в C#:
class MyClass
{
int x;
double y;
bool Initialized;
// Когато бъде създаден нов обект от класа MyClass конструктурът ще бъде извикан и ще инициализира обекта.
public MyClass()
{
x = 5;
y = 0.5;
Initialized = true;
}
}
Деструктори
редактиранеДеструктор е метод, който се извиква автоматично, когато обект от даден клас приключи жизнения си цикъл. Деструкторите се ползват за освобождаване на ресурси заети от обекта или за други задачи свързани с унищожаването на обекта. В повечето езици деструкторите се извикват без параметри и не връщат стойност.
В езика C# деструкторите нямат широко приложение, тъй като мениджърът на паметта (garbage collector) има грижата да освободи всеки блок памет, към който не сочи използващ се при програмата указател. Ако някой метод задели определено количество памет за масив или структура от данни, след приключване на работата на метода тази заделена памет остава „висяща“, към нея вече не сочи никакъв указател и при следващото минаване garbage collector-а ще я освободи.
Но за разлика от паметта, заети други ресурси — например мрежови връзки, файлове или заявки към бази данни — не се освобождават от garbage collector-а и за тяхното освобождаване трябва да се предприемат някакви мерки. Най-често се ползва using структурата, която автоматично затваря всички връзки щом като се изпълни using блока. Пример:
using (StreamReader reader = new StreamReader("input.txt"))
{
извършват се някакви операции върху файла "input.txt"
}// тук файлът се затваря автоматично и всички ресурси свързани с работата му се освобождават
reader.ReadLine(); // тази команда ще предизвика грешка, тъй като файла вече не е на разположение
Методи за присвояване
редактиранеТези методи могат да дефинират или предефинират вече съществуващ метод, който да определя, какви операции трябва да се извършат, когато на обект от даден клас бъде присвоен друг обект от същия клас.
Методи Оператори
редактиранеТези методи дефинират или предефинират вече съществуващ метод, като посочват какви операции трябва да се извършат с подадените като параметри обекти при дадена операция. Примерно за дефиниран от потребителя клас, операцията събиране (+) може да бъде предефинирана, така че да се извършат специфични действия, когато тази операция се използва върху два обекта от този клас.
class MyClass
{
int x;
double y;
// Конструктор на класа, който позволява инициализацията на членовете на класа x и y със зададени параметри.
public MyClass(int integer, double floatingpoint)
{
x = integer;
y = floatingpoint;
}
public static MyClass operator +(MyClass op1, MyClass op2)
{
return new MyClass(op1.x + op2.x, op1.y * op2.y);
// Операторът + за този клас ще връща нов обект от класа, чийто член x ще е равен на сбора от членовете x на двата операнда,
// но членът y ще е равен на умножението на членовете y на двата операнда.
}
}
Литература
редактиране- Светлин Наков, Веселин Колев и колектив. Въведение в програмирането със C#. Велико Търново, Фабер, 2011. ISBN 978-954-400-527-6.