Параметри методів. Модифікатори ref і out. Приклади. Відмінності між модифікаторами ref і out

Параметри методів. Модифікатори ref і out. Приклади. Відмінності між модифікаторами ref і out


Зміст



1. Застосування модифікатора параметру ref. Яке призначення модифікатора параметру ref?

Модифікатор ref призначений для вказання того, що параметр методу повинен передаватись за посиланням а не за значенням. Іншими словами, якщо в метод потрібно примусово передати аргумент за посиланням, то при оголошенні методу перед відповідним формальним параметром потрібно вказати модифікатор ref.

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

ref type param

Загальна форма методу в класі, що отримує формальний параметр ref наступна:

access return_type MethodName(ref type param)
{
    // ...
}

де

  • MethodName – ім’я методу;
  • access – тип доступу до методу (private, protected, public, internal);
  • return_type – тип, що повертає метод;
  • type – тип параметру з іменем param, який отримує метод;
  • param – ім’я формального параметру.

При виклику такого методу з іншого коду, перед параметром також ставиться модифікатор ref:

MethodName(ref argument);

де argument – аргумент, що передається в метод. Цього вимагає синтаксис

 

2. Приклади методів, що використовують модифікатор параметру ref

Приклад 1. Реалізація методу, що множить число на 5. Число є вхідним параметром методу. Метод отримує значення числа, множить його на 5 і повертає завдяки модифікатору ref. Тобто метод збільшує самий аргумент у 5 разів.

// метод, що збільшує параметр x у 5 разів
public void Mult5(ref int x)
{
    x = x * 5;
}

Виклик методу Mult5() з іншого програмного коду

int d = 10;
qe.Mult5(ref d); // d = 10*5 = 50

Після виклику, значення змінної d стає рівне 50. Згідно синтаксису C#, перед викликом змінної d потрібно ставити модифікатор параметру ref.

Приклад 2. Розв’язок квадратного рівняння. Задано клас QuadraticEquation, що містить дані та метод розв’язку квадратного рівняння.
У класі реалізовано метод Calc(), який отримує два параметри x1, x2 з модифікатором ref. Ці параметри змінюють свої значення у методі, у випадку, якщо рівняння має розв’язок.

// клас, що реалізує дані та метод розв'язку квадратного рівняння
class QuadraticEquation
{
    public double a, b, c;

    // конструктор класу, отримує параметрами коефіцієнти a, b, c рівняння
    public QuadraticEquation(double _a, double _b, double _c)
    {
        if (IsSolution(a, b, c))
        {
            a = _a;
            b = _b;
            c = _c;
        }
        else
            a = b = c = 0;
    }

    // внутрішній метод, що визначає, чи рівняння має розв'язок
    bool IsSolution(double a, double b, double c)
    {
        double d = b * b - 4 * a * c;
        if (d < 0) return false;
        return true;
    }

    // Метод, що розв'язує квадратне рівняння
    // Метод повертає true та результат в x1, x2, якщо рівняння має розв'язок,
    // в іншому випадку метод повертає false
    public bool Calc(ref double x1, ref double x2)
    {
        // перевірка, чи рівняння має розв'язок
        if (!IsSolution(a, b, c))
        return false;

        // якщо розв'язок є, то обчислення x1, x2
        double d = b * b - 4 * a * c;
        x1 = (-b - Math.Sqrt(d)) / (2 * a);
        x2 = (-b + Math.Sqrt(d)) / (2 * a);

        return true;
    }
}

Виклик методу з іншого класу може бути таким як описано нижче

class Program
{
    static void Main(string[] args)
    {
        // створення об'єкту класу QuadraticEquation
        QuadraticEquation qe = new QuadraticEquation(2, -8, 5);

        double x1 = 0, x2 = 0;

        if (qe.Calc(ref x1, ref x2)) // виклик методу Calc(), перед x1, x2 задається модифікатор ref
        {
            // якщо є розв'язок, то вивести корені рівняння x1, x2
            Console.WriteLine("x1 = {0}", x1);
            Console.WriteLine("x2 = {0}", x2);
        }
        else
            Console.WriteLine("Рівняння не має коренів.");
        return;
    }
}

Варто зауважити, що при виклику методу Calc() обов’язково вказуються модифікатори ref:

Calc(ref x1, ref x2)

Приклад 3. Дано рядок s. Розробити метод, що видаляє з рядка s заданий символ c. Символ c є вхідним параметром-значенням. Рядок s повинна бути параметром-посиланням та результатом.
Нижче наведено клас Str з реалізацією методу DeleteSymbol(). Також продемонстровано виклик методу DeleteSymbol() з функції Main().

// клас, що реалізує метод, який повертає назву цифри
class Str
{
    // метод видаляє з рядка s символ c
    public void DeleteSymbol(ref string s, char c)
    {
        string s2="";
        for (int i=0; i<s.Length; i++)
            if (s[i] != c)
                s2 = s2 + s[i];
        s = s2;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Str S = new Str();
        string str = "This is a textbook.";

        // виклик методу DeleteSymbol
        S.DeleteSymbol(ref str, 'i'); // str = "Ths s a textbook."
        Console.WriteLine(str);
        return;
    }
}

 

3. Застосування модифікатора параметру out. Яке призначення модифікатора параметру out?

Модифікатор out використовується, якщо потрібно виконання двох умов:

  • методу не потрібно передавати значення;
  • метод обов’язково повинен повертати значення з допомогою параметру.

Модифікатор out для параметру з іменем param типу type вказується на початку його оголошення

out type param

Загальна форма методу, що отримує один параметр з модифікатором out має вигляд

access return_type MethodName(out type param)
{
    // ...
}

де

  • MethodName – ім’я методу;
  • access – тип доступу до методу (private, protected, public, internal);
  • return_type – тип, що повертає метод;
  • type – тип параметру з іменем param, який отримує метод;
  • param – ім’я формального параметру.

 

4. Приклади методів, що використовують модифікатор параметру out

Приклад 1. Розробити метод, що повертає число Авогадро. Число Авогадро задається параметром методу.
Текст методу:

// число Авогадро
void GetAvogadro(out double Avg)
{
    Avg = 6.022140857e23; 
}

Виклик методу з іншого програмного коду

// виклик методу GetAvogadro() з іншого методу
double Avg;
GetAvogadro(out Avg); // вказання модифікатора out - обов'язкове
// Avg = 6.022140857E+23

Як видно з вищенаведеного коду, при виклику методу, що містить out-параметр, обов’язково потрібно вказувати модифікатор out

GetAvogadro(out Avg);

Приклад 2. Розробити метод CalcEquation(), що розв’язує квадратне рівняння. Метод отримує вхідними параметрами коефіцієнти рівняння a, b, c. Ці параметри передаються в рівняння за значеннями.
Метод повертає розв’язок рівняння з допомогою параметрів x1, x2, які оголошені з модифікатором out.
Метод повертає true з допомогою оператора return, якщо рівняння має розв’язок. В іншому випадку, метод повертає false.

Нижче наведено клас Program, в якому реалізовано метод CalcEquation().

class Program
{
    static bool CalcEquation(double a, double b,  double c, out double x1, out double x2)
    {
        double d;
        d = b * b - 4 * a * c;
        if (d>=0)
        {
            x1 = (-b - Math.Sqrt(d)) / (2 * a);
            x2 = (-b + Math.Sqrt(d)) / (2 * a);
            return true;
        }
        else
        {
            x1 = x2 = 0; // обов'язково, інакше помилка
            return false;
        }
    }

    static void Main(string[] args)
    {
        // Демонстрація використання модифікатора параметру out
        bool res;
        double x1, x2;

        // виклик методу
        res = CalcEquation(8, 3, -4, out x1, out x2);

        if (res)
        {
            Console.WriteLine("x1 = {0}", x1);
            Console.WriteLine("x2 = {0}", x2);
        }
        else
            Console.WriteLine("Рівняння не має коренів");
        return;
    }
}

Приклад 3. Розробити метод, що повертає назву цифри у рядку.

// клас, що реалізує метод, який повертає назву цифри
class Number
{ 
    // вивід назви числа на основі вхідного значення num
    public void TextNumber(int num, out string text)
    {
        switch (num)
        {
            case 1: text = "one"; break;
            case 2: text = "two"; break;
            case 3: text = "three"; break;
            case 4: text = "four"; break;
            case 5: text = "five"; break;
            case 6: text = "six"; break;
            case 7: text = "seven"; break;
            default: text = ""; break;
        } 
        return;
    }
}

Використання методу TextNumber() класу Number

Number nm = new Number(); // nm - об'єкт класу Number
string s;

nm.TextNumber(3, out s); // s = "three"
nm.TextNumber(8, out s); // s = ""
nm.TextNumber(1, out s); // s = "one"

 

5. Яка відмінність між модифікаторами ref та out?

Між модифікаторами ref та out є три взаємопов’язані відмінності:

  • 1. Параметр з модифікатором out використовується тільки для повернення значення з методу. Параметр з модифікатором ref може використовуватись і для повернення і для задавання значення в методі іншим змінним. Тому, перед викликом методу, немає потреби присвоювати якесь значення змінній, що використовується з модифікатором out.
  • 2. В методі, змінна оголошена з параметром out вважається неініціалізованою. Змінна, оголошена з ref вважається ініціалізованою. Тому, не можна використовувати out-змінну у правій частині оператора присвоювання. А ref-змінну можна.
  • 3. Якщо параметр оголошено з модифікатором out, то в тілі методу цьому параметру обов’язково має бути присвоєне якесь значення. Інакше буде помилка компіляції. Якщо параметр оголошено з модифікатором ref, то цьому параметру присвоювати значення в тілі методу не обов’язково.

 


Зв’язані теми