При разработке программ, некоторые строки с инструкциями повторяются, для того, чтобы уменьшить количество кода, улучшить его читабельность и упростить повторное использование этих инструкций, их можно объединить в метод.
Метод – это конструкция в виде блока кода, которая выполняет некоторые действия, и имеет логическую смысловую нагрузку. Аналогом методов языка 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.