Параметри методів. Модифікатори ref і out. Приклади. Відмінності між модифікаторами ref і out
Зміст
- 1. Застосування модифікатора параметру ref. Яке призначення модифікатора параметру ref?
- 2. Приклади методів, що використовують модифікатор параметру ref
- 3. Застосування модифікатора параметру out. Яке призначення модифікатора параметру out?
- 4. Приклади методів, що використовують модифікатор параметру out
- 5. Яка відмінність між модифікаторами ref та out?
- 6. Передача у функцію посилання на екземпляр класу з використанням модифікатору ref. Приклад
- 7. Передача у функцію посилання на екземпляр класу з використанням модифікатору 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, то цьому параметру присвоювати значення в тілі методу не обов’язково.
⇑
6. Передача у функцію посилання на екземпляр класу з використанням модифікатору ref. Приклад
У функцію можна передавати не тільки значення базових типів (int, double, char і т.д.) але і значення екземплярів класу (об’єктів). Якщо при передачі у функцію об’єкту класу використовуються модифікатори ref або out, то саме посилання передається за посиланням. Це дозволяє змінювати сам об’єкт у методі.
Приклад. У прикладі реалізовано метод, який замінює один об’єкт класу на інший. Обидва об’єкти класу передаються у метод як параметри. Об’єкт який потрібно замінити передається з модифікатором ref.
using System; using static System.Console; namespace ConsoleApp2 { // Клас Point - описує точку на координатній площині class Point { // внутрішні поля класу int x; int y; // Конструктор public Point(int x, int y) { this.x = x; this.y = y; } // Метод присвоєння одного екземпляру класу іншому: // point1 <= point2 public void AssignmentPoint(ref Point point1, Point point2) { point1 = point2; // перевизначення об'єкту point1 } // Метод, що виводить значення полів класу public void Print(string text) { Write(text); WriteLine("x = {0}, y = {1}", x, y); } } class Program { static void Main(string[] args) { // Передача посилань на об'єкти як ref- та out-параметрів // 1. Оголосити екземпляри класів Point pt1 = new Point(1, 2); Point pt2 = new Point(5, 6); // 2. Вивести екземпляри класів pt1.Print("Object pt1:"); pt2.Print("Object pt2:"); // 3. Виклик методу AssignmentPoint() - передача об'єкту як ref-посилання pt1.AssignmentPoint(ref pt1, pt2); // pt1 <= pt2 // 4. Вивести екземпляри класів після присвоєння WriteLine("\nAfter AssignmentPoint"); pt1.Print("Object pt1:"); pt2.Print("Object pt2:"); } } }
Результат виконання програми
Object pt1:x = 1, y = 2 Object pt2:x = 5, y = 6 After AssignmentPoint Object pt1:x = 5, y = 6 Object pt2:x = 5, y = 6
Проаналізувавши результат роботи програми можна прийти до висновку: у методі AssignmentPoint() відбулась повна заміна об’єкту pt1 на об’єкт pt2. Це можливо завдяки модифікатору ref. Якщо забрати модифікатор ref з першого параметру методу AssignmentPoint(), тоді зміни всередині методу не будуть діяти у викликаючому коді (функції main()). Щоб перевірити це, достатньо забрати модифікатор ref у методі AssignmentPoint() та у виклику цього методу.
⇑
7. Передача у функцію посилання на екземпляр класу з використанням модифікатору out. Приклад
Екземпляр класу може бути переданий у функцію з модифікатором out. При такій передачі можна змінювати сам екземпляр класу як і у випадку з ref-модифікатором.
Приклад. У прикладі реалізовано метод FormArray(), який формує новий об’єкт класу ArrayDouble, виділяючи для нього пам’ять та заповнюючи довільним значенням. У викликаючому коді (функції main()) метод отримує екземпляр класу зі значенням null.
using System; using static System.Console; namespace ConsoleApp2 { // Клас, що реалізує масив чисел class ArrayDouble { private double[] A; // масив чисел // Конструктор з 1 параметром public ArrayDouble(int length) { // виділити пам'ять для масиву та заповнити його нульовими значеннями A = new double[length]; for (int i = 0; i < A.Length; i++) { A[i] = 0.0; } } // Методи доступу public double GetAi(int index) { if ((index >= 0) && (index < A.Length)) return A[index]; else return 0; } public void SetAi(int index, double value) { if ((index >= 0) && (index < A.Length)) A[index] = value; } // Метод, що виводить масив в рядок public void Print(string text) { WriteLine(text); for (int i = 0; i < A.Length; i++) { Write("{0:f2}\t", A[i]); } WriteLine(); } } class Program { // Метод, який заповнює масив типу ArrayDouble випадковими числами, // метод формує повністю новий екземпляр класу ArrayDouble static void FormArray(out ArrayDouble A, int length) { // 1. Ініціалізувати генератор випадкових чисел Random rnd_num = new Random(); // 2. Виділити пам'ять для екземпляру A A = new ArrayDouble(length); // 3. Заповнити екземпляр A випадковими числами від 0 до 10 for (int i = 0; i < length; i++) { A.SetAi(i, rnd_num.NextDouble() * 10); } } static void Main(string[] args) { ArrayDouble ad = null; // екземпляр класу ArrayDouble // Сформувати масив ad // параметр out - масив формується всередині методу FormArray() Program.FormArray(out ad, 5); ad.Print("1 - Array ad:"); // Повторно сформувати масив ad FormArray(out ad, 7); // на виході з методу - новий екземпляр ad.Print("2 - Array ad:"); } } }
Результат виконання програми
1 - Array ad: 4.67 8.74 2.64 4.83 0.34 2 - Array ad: 6.65 6.66 4.32 1.24 8.30 9.73 3.13
⇑
Зв’язані теми
- Поняття методу. Методи в класах. Повернення з методу. Оператор return. Методи без параметрів. Ключове слово void
- Передача параметрів у метод. Аргументи і формальні параметри. Приклади. Передачапосилання на об’єкт класу в метод
- Змінна кількість аргументів у методах. Модифікатор params. Переваги. Приклади методів зі змінною кількістю аргументів
- Передача масивів у методи. Приклади. Передача масивів структур, класів, зчислень у методи. Передача двовимірних масивів у методи. Повернення масиву з методу
- Необов’язкові аргументи. Переваги. Приклади застосування необов’язкових аргументів. Неоднозначність в необов’язкових аргументах
- Іменовані аргументи. Переваги використання. Приклади