Під час написання програм, деякі рядки з інструкціями повторюються, для того, щоб зменшити кількість коду, покращити читабельність та спростити повторне використання цих інструкцій, їх можна об’єднати в метод.

Метод – це конструкція у вигляді блоку коду, яка виконує певні дії, та має закінчене смислове навантаження. Аналогом методів мови C#, є математичні функції.

В деяких мовах програмування, розділяють функції та процедури. В мові C#, такого розділення немає, хоча умовно можна вважати функціями методи, які повертають значення, а процедурами – методи, що не повертають значень.

Синтаксично метод виглядає наступним чином:

<modifiers> type name(<parameters>)
{
    //тіло методу
}
  • modifiers – необов’язкові модифікатори доступу;
  • type – тип даних, які повертаються:
    • якщо метод не повертає значень, то використовується тип void;
    • якщо повертає, то в тілі методу, повинно бути присутнє, ключове слово return, після якого вказують значення(того ж типу, що і метод), яке повертається;
  • name – ім’я методу;
  • parameters – необов’язкові параметри(аргументи).

Розглянемо приклади методів:

using System;

class Program
{
    static void PrintHelloWorld()
    {
        Console.WriteLine("Hello World!");
    }

    static void PrintHello(string name)
    {
        var text = "Hello " + name + "!";
        Console.WriteLine(text);
    }

    static int Cube(int x)
    {
        return x * x * x;
    }

    static void Main(string[] args)
    {
        PrintHelloWorld();
        PrintHello("Andrew");
        var b1 = Cube(2); //8
        var b2 = Cube(3); //27

        Console.ReadLine();
    }
}

Всі три методи викликаються в основному методі програми – Main. Вони мають модифікатори доступу static. Перші два методи типу void, тобто нічого не повертають.
PrintHelloWorld – викликає Console.WriteLine з текстовим параметром.
PrintHello – приймає в якості аргументу текстовий рядок і після модифікації тексту, передає його в інший метод Console.WriteLine, який виводить текст в консоль.
Cube – приймає на вхід ціле число, підносить його до кубу, та повертає результат.

Коли використовувати методи

Якщо частина коду повторюється два і більше рази, то є сенс винести інструкції в окремий метод. Розглянемо приклад вводу масиву з клавіатури:

var a = new int[10];
var b = new int[7];

for (int i = 0; i < a.Length; i++)
{
    Console.Write($"a[{i}] = ");
    a[i] = Convert.ToInt32(Console.ReadLine());
}

for (int i = 0; i < b.Length; i++)
{
    Console.Write($"b[{i}] = ");
    b[i] = Convert.ToInt32(Console.ReadLine());
} 

Як бачимо, код для заповнення масивів дублюється, тому ми можемо створити метод для заповнення масиву значеннями, що вводяться з клавіатури:

static int[] GetArrayFromConsole(string arrayName, int elementCount)
{
    var retVal = new int[elementCount];

    for (int i = 0; i < retVal.Length; i++)
    {
        Console.Write($"{arrayName}[{i}] = ");
        retVal[i] = Convert.ToInt32(Console.ReadLine());
    }

    return retVal;
}

Як аргументи, методу передаються: назва масиву та кількість елементів(довжина).

Після цього, метод можна багаторазово використовувати, для створення нових масивів цілих чисел, які вводить користувач:

static void Main(string[] args)
{
    int[] t = GetArrayFromConsole("t", 5);
    var g = GetArrayFromConsole("g", 4);
}

Оператор return

Оператор return завершує виконання методу, та може повертати значення. Інструкції розміщені після return ігноруються. В методі дозволяється багатократне використання оператора return, однак інколи це ускладнює читання коду програми. Якщо метод має тип void – оператор return може використовуватись для дострокового виходу:

static void Test(int i)
{
    if (i <= 0 || i > 5)
    {
        return;
        Console.Write("123"); //цей рядок завжди ігнорується
    }

    Console.WriteLine(i);
}

static string ZeroCompare(double number)
{
    if (number < 0)
    {
        return "Число менше нуля";
    }
    else if (number > 0)
    {
        return "Число більше нуля";
    }

    return "Число рівне нулю";
}

Параметри методів

Параметри методу дозволяють передавати в нього деякі вхідні дані. Під час виклику методу, параметри передаються в тому ж порядку, в якому вони описані. Тобто значення аргументу присвоюється параметру в залежності від його позиції в списку параметрів.

Передача параметрів по значенню

Для примітивних типів даних передача параметрів відбувається по значенню. Тобто в якості аргументу передається копія даних. В тілі методу, ми можемо змінювати змінні параметрів, при цьому зміни ніяк не впливають на передані дані:

using System;

class Program
{
    static int Add(int x1, int x2)
    {
        x1 += x2; //зміна x1 не впливає на передану в якості аргументу змінну num1
        return x1;
    }

    static void Main(string[] args)
    {
        var num1 = 2;
        var num2 = 5;
        var sum = Add(num1, num2);

        Console.WriteLine($"{num1} + {num2} = {sum}");
        Console.ReadLine();
    }
}

Передача параметрів по посиланню

Для передачі змінних за посиланням, використовується модифікатор ref. Якщо параметр позначений ключовим словом ref, то будь які зміни цього параметру в тілі методу, відображаються на передану змінну:

using System;

class Program
{
    static int Mult(ref int x1, int x2)
    {
        x1 *= x2; //зміна x1 впливає на змінну n1
        return x1;
    }

    static void Main(string[] args)
    {
        var n1 = 24;
        var n2 = 4;
        var mult = Mult(ref n1, n2);
        //змінна n1 містить нове значення
        Console.WriteLine($"{n1}");
        Console.ReadLine();
    }
}

Модифікатор ref вказується як в описі, так і при виклику методу.

Вихідні параметри

Вище розглянуті параметри мають назву вхідні, оскільки передаються на вхід методу. Крім цього, в мові C#, є вихідні параметри. Для того, щоб позначити параметр як вихідний, використовується модифікатор out.

Розглянемо метод для ділення двох чисел, він повертає текст помилки, якщо ділення не можливе, а в якості вихідного параметру – результат операції:

using System;

class Program
{
    static string Div(int a, int b, out int result)
    {
        if (b == 0)
        {
            result = int.MinValue;
            return "На нуль ділити не можна!";
        }
        else
        {
            result = a / b;
            return "";
        }
    }

    static void PrintResult(string errorText, int res)
    {
        if (string.IsNullOrEmpty(errorText))
        {
            Console.WriteLine(res);
        }
        else
        {
            Console.WriteLine(errorText);
        }
    }

    static void Main(string[] args)
    {
        int r1;
        var err1 = Div(64, 8, out r1);
        PrintResult(err1, r1);

        //змінна r2 оголошується в списку аргументів 
        var err2 = Div(34, 0, out int r2); 
        PrintResult(err2, r2);

        Console.ReadLine();
    }
}

Як видно з прикладу, C# 7.0 підтримує дві рівнозначні форми синтаксису для запису вихідних параметрів:

  • змінна оголошується перед викликом методу;
  • змінна оголошується в списку аргументів.

Вихідному параметру, в тілі методу, обов’язково повинно бути присвоєне значення.
Як і у випадку з ref, модифікатор out вказується в описі та при виклику методу.

Іменовані аргументи

В C# є можливість передавати параметри в довільному порядку, для цього використовуються іменовані аргументи(named arguments). Щоб явно вказати ім’я аргументу, використовується наступний синтаксис:

arg_name : arg_value

Приклад програми з іменованими аргументами:

static string GetPrettyName(string firstName, string lastName)
{
    return firstName + " " + lastName;
}

static void Main(string[] args)
{
    var s = GetPrettyName(lastName: "Jackson", firstName: "Anna");
}

Опціональні параметри

В методах можна використовувати необов’язкові чи опціональні параметри, які мають задане за змовчуванням значення.

Розглянемо приклад методу, для розрахунку процентів по депозитному вкладу в банку. Зазвичай депозитна ставка 10%, крім того постійні клієнти можуть отримати бонус:

static decimal CalcDeposit(decimal sum, decimal percent = 10m, decimal bonus = 0m)
{
    return sum + sum * ((percent + bonus) / 100m);
}

Приклад використання розрахунку:

//депозит з ставкою та бонусом за замовчуванням
var d1 = CalcDeposit(10000); 
//нараховано бонус 2.5% (передається як іменований аргумент), ставка за замовчуванням - 10%
var d2 = CalcDeposit(15000, bonus: 2.5m); 
//збільшено процентну ставку та бонус
var d3 = CalcDeposit(20000, 15m, 3m); 

Необов’язкові параметри повинні бути останніми у списку аргументів методу.

Скорочений запис методів

Починаючи з C# 6.0, методи, що містять тільки одну інструкцію, можна записувати в скороченій формі через лямбда-вирази, що дає змогу значно скоротити кількість написаного коду.

Наприклад математичну функцію y = x2 - 2/x, можна записати:

static double Y(double x)
{
    return x * x - 2 / x;
}

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

static double Y(double x) => x * x - 2 / x;

Аналогічним чином можна спрощувати методи, які не повертають значень:

static void DisplayHello(string n)
{
    Console.WriteLine("Привіт {0}!", n);
}

Скорочується до:

static void DisplayHello(string n) => Console.WriteLine("Привіт {0}!", n);

Локальні функції

В версії C# 7.0, з’явилась можливість використовувати методи вкладені в метод, такі конструкції мають назву – локальні функції.

Вони підтримують як класичну, так і спрощену форму синтаксису:

static void Main(string[] args)
{
    //максимальне з цілих двох чисел
    int Max(int a, int b)
    {
        if (a > b)
            return a;
        return b;
    }
    
    // мінімальне з двох цілих чисел
    int Min(int c, int d) => c < d ? c : d;

    var t1 = Max(13, 22);
    var t2 = Min(42, 56);
}

Для всіх розглянутих методів використовується модифікатор static, оскільки передбачається їх безпосереднє використання в головному методі програми – Main.

Дивіться також: