Реализация методов и параметров класса
Метод — это блок кода, содержащий ряд операторов. Программа приводит к выполнению инструкций путем вызова метода и указания любых обязательных аргументов метода. В C#каждая выполненная инструкция выполняется в контексте метода.
Сигнатуры метода
Методы объявляются в классе, записи или структуре путем указания:
- Необязательный уровень доступа, например
publicилиprivate. Значение по умолчанию —private. - Необязательные модификаторы, такие как
abstractилиsealed. - Возвращаемое значение или
void, если метод отсутствует. - Имя метода.
- Все параметры метода. Параметры метода заключены в скобки и разделены запятыми. Пустые скобки указывают, что метод не требует параметров.
Эти части работают вместе, чтобы сформировать сигнатуру метода.
Важный
Возвращаемый тип метода не является частью сигнатуры метода в целях перегрузки метода. Однако она является частью сигнатуры метода при определении совместимости между делегатом и методом, на который он указывает.
В следующем примере определяется класс с именем Motorcycle, содержащий пять методов:
namespace MotorCycleExample
{
abstract class Motorcycle
{
// Anyone can call this.
public void StartEngine() {/* Method statements here */ }
// Only derived classes can call this.
protected void AddGas(int gallons) { /* Method statements here */ }
// Derived classes can override the base class implementation.
public virtual int Drive(int miles, int speed) { /* Method statements here */ return 1; }
// Derived classes can override the base class implementation.
public virtual int Drive(TimeSpan time, int speed) { /* Method statements here */ return 0; }
// Derived classes must implement this.
public abstract double GetTopSpeed();
}
}
Класс Motorcycle включает перегруженный метод Drive. Два метода Drive имеют одинаковое имя, но разные типы параметров.
Вызов метода
Методы могут быть экземпляром или статическими . Необходимо создать экземпляр объекта для вызова метода экземпляра в этом экземпляре; Метод экземпляра работает с этим экземпляром и его данными. Вы вызываете статический метод, ссылаясь на имя типа, к которому принадлежит метод; статические методы не работают с данными экземпляра. Попытка вызвать статический метод через экземпляр объекта создает ошибку компилятора.
Вызов метода похож на доступ к полю. После имени объекта (при вызове метода экземпляра) или имени типа (если вы вызываете статический метод), добавьте период, имя метода и скобки. Аргументы перечислены в скобках и разделены запятыми.
Определение метода задает имена и типы всех необходимых параметров. Когда вызывающий вызывает метод, он предоставляет конкретные значения, называемые аргументами для каждого параметра. Аргументы должны быть совместимы с типом параметра, но имя аргумента, если он используется в вызывающем коде, не должен совпадать с параметром, указанным в методе. В следующем примере метод Square включает один параметр типа int с именем i. Первый вызов метода передает метод Square переменную типа int с именем num; вторая, числовая константа; и третье выражение.
public static class SquareExample
{
public static void Main()
{
// Call with an int variable.
int num = 4;
int productA = Square(num);
// Call with an integer literal.
int productB = Square(12);
// Call with an expression that evaluates to int.
int productC = Square(productA * 3);
}
static int Square(int i)
{
// Store input argument in a local variable.
int input = i;
return input * input;
}
}
Параметры значений и ссылок
Типы в C# — это типы значений или ссылочные типы. По умолчанию типы значений и ссылочные типы передаются по значению в метод.
Параметры типа значения
Когда тип значения передается методу по значению, копия объекта вместо самого объекта передается методу. Поэтому изменения объекта в вызываемом методе не влияют на исходный объект, когда элемент управления возвращается вызывающей стороне.
В следующем примере передается тип значения методу по значению, а вызываемый метод пытается изменить значение типа значения. Он определяет переменную типа int, которая является типом значения, инициализирует его значение до 20 и передает его методу с именем ModifyValue, который изменяет значение переменной на 30. Однако при возврате метода значение переменной остается неизменным.
public static class ByValueExample
{
public static void Main()
{
var value = 20;
Console.WriteLine("In Main, value = {0}", value);
ModifyValue(value);
Console.WriteLine("Back in Main, value = {0}", value);
}
static void ModifyValue(int i)
{
i = 30;
Console.WriteLine("In ModifyValue, parameter value = {0}", i);
return;
}
}
// The example displays the following output:
// In Main, value = 20
// In ModifyValue, parameter value = 30
// Back in Main, value = 20
Когда объект ссылочного типа передается методу по значению, ссылка на объект передается по значению. То есть метод получает не сам объект, а аргумент, указывающий расположение объекта. Если изменить элемент объекта с помощью этой ссылки, это изменение отражается в объекте, когда элемент управления возвращается в вызывающий метод. Однако замена объекта, переданного методу, не влияет на исходный объект, когда элемент управления возвращается вызывающей стороне.
В следующем примере определяется класс (который является ссылочным типом) с именем SampleRefType. Он создает экземпляр объекта SampleRefType, назначает 44 value поле и передает объект в метод ModifyObject. Этот пример по сути совпадает с предыдущим примером (он передает аргумент по значению методу). Однако результат отличается, так как используется ссылочный тип, а не тип значения. Изменения, внесенные в ModifyObject в поле obj.value, также изменяют поле value аргумента rt. Когда метод Main отображает значение rt мы видим, что оно обновлено до 33, как показано в примере.
public class SampleRefType
{
public int value;
}
public static class ByRefTypeExample
{
public static void Main()
{
var rt = new SampleRefType { value = 44 };
Console.WriteLine("In Main, rt.value = {0}", rt.value);
ModifyObject(rt);
Console.WriteLine("Back in Main, rt.value = {0}", rt.value);
}
static void ModifyObject(SampleRefType obj)
{
obj.value = 33;
Console.WriteLine("In ModifyObject, obj.value = {0}", obj.value);
}
}
// The example displays the following output:
// In Main, rt.value = 44
// In ModifyObject, obj.value = 33
// Back in Main, rt.value = 33
Параметры ссылочного типа
Вы передаете параметр по ссылке, если вы хотите изменить значение аргумента в методе и хотите отразить это изменение при возврате элемента управления в вызывающий метод. Чтобы передать параметр по ссылке, используйте ключевое слово ref или out. Можно также передать значение по ссылке, чтобы избежать копирования, но по-прежнему предотвращать изменения с помощью ключевого слова in.
Следующий пример идентичен предыдущему, за исключением того, что значение передается ссылкой на метод ModifyValue. Когда значение параметра изменяется в методе ModifyValue, изменение значения отражается при возврате элемента управления вызывающей стороне.
public static class ByRefExample
{
public static void Main()
{
var value = 20;
Console.WriteLine("In Main, value = {0}", value);
ModifyValue(ref value);
Console.WriteLine("Back in Main, value = {0}", value);
}
private static void ModifyValue(ref int i)
{
i = 30;
Console.WriteLine("In ModifyValue, parameter value = {0}", i);
return;
}
}
// The example displays the following output:
// In Main, value = 20
// In ModifyValue, parameter value = 30
// Back in Main, value = 30
Распространенный шаблон, используемый параметрами ссылок, включает переключение значений переменных. Вы передаете два переменных методу по ссылке, а метод переключает их содержимое. В следующем примере заменяются целые значения.
public static class RefSwapExample
{
static void Main()
{
int i = 2, j = 3;
Console.WriteLine("i = {0} j = {1}", i, j);
Swap(ref i, ref j);
Console.WriteLine("i = {0} j = {1}", i, j);
}
static void Swap(ref int x, ref int y) =>
(y, x) = (x, y);
}
// The example displays the following output:
// i = 2 j = 3
// i = 3 j = 2
Передача параметра ссылочного типа позволяет изменить значение самой ссылки, а не значение отдельных элементов или полей.
Коллекции параметров
Иногда необходимо указать точное количество аргументов для метода. Используя ключевое слово params, чтобы указать, что параметр является коллекцией параметров, можно вызвать метод с переменным числом аргументов. Параметр, помеченный ключевым словом params, должен быть типом коллекции, и он должен быть последним параметром в списке параметров метода.
Вызывающий объект может вызвать метод в любом из четырех способов для параметра params:
- Передав коллекцию соответствующего типа, содержащую требуемое количество элементов. В примере используется выражение коллекции, поэтому компилятор создает соответствующий тип коллекции.
- Передав список отдельных аргументов соответствующего типа в метод, разделенный запятыми. Компилятор создает соответствующий тип коллекции.
- Передав
null. - Не предоставляя аргумент коллекции параметров.
В следующем примере определяется метод с именем GetVowels, который возвращает все гласные из коллекции параметров. Метод Main иллюстрирует все четыре способа вызова метода. Вызывающие элементы не требуются для предоставления аргументов для параметров, включающих модификатор params. В этом случае параметр является пустой коллекцией.
static class ParamsExample
{
static void Main()
{
string fromArray = GetVowels(["apple", "banana", "pear"]);
Console.WriteLine($"Vowels from collection expression: '{fromArray}'");
string fromMultipleArguments = GetVowels("apple", "banana", "pear");
Console.WriteLine($"Vowels from multiple arguments: '{fromMultipleArguments}'");
string fromNull = GetVowels(null);
Console.WriteLine($"Vowels from null: '{fromNull}'");
string fromNoValue = GetVowels();
Console.WriteLine($"Vowels from no value: '{fromNoValue}'");
}
static string GetVowels(params IEnumerable<string>? input)
{
if (input == null || !input.Any())
{
return string.Empty;
}
char[] vowels = ['A', 'E', 'I', 'O', 'U'];
return string.Concat(
input.SelectMany(
word => word.Where(letter => vowels.Contains(char.ToUpper(letter)))));
}
}
// The example displays the following output:
// Vowels from array: 'aeaaaea'
// Vowels from multiple arguments: 'aeaaaea'
// Vowels from null: ''
// Vowels from no value: ''
Заметка
До C# 13 модификатор params можно использовать только с одниммерным массивом.
Возвращаемые значения методов
Методы могут возвращать вызывающему объекту значение. Если возвращаемый тип (тип, указанный перед именем метода), не void, метод может вернуть значение с помощью ключевого слова return. Оператор с ключевым словом return, за которым следует переменная, константа или выражение, которое соответствует типу возвращаемого значения, возвращает это значение вызывающей функции метода. Методы с невоидным типом возвращаемого значения требуются для использования ключевого слова return для возврата значения. Ключевое слово return также останавливает выполнение метода.
Если возвращаемый тип void, оператор return без значения по-прежнему полезен для остановки выполнения метода. Без ключевого слова return метод перестает выполняться, когда он достигает конца блока кода.
Например, эти два метода используют ключевое слово return для возврата целых чисел:
class SimpleMath
{
public int AddTwoNumbers(int number1, int number2) =>
number1 + number2;
public int SquareANumber(int number) =>
number * number;
}
В этом примере используются элементы с выражением, назначаемые возвращаемым значением методов. Этот синтаксис является коротким для методов, использующих одну инструкцию (выражение) для вычисления возвращаемого значения.
Вы также можете определить методы с текстом инструкции и оператором return:
class SimpleMathExtnsion
{
public int DivideTwoNumbers(int number1, int number2)
{
return number1 / number2;
}
}
Чтобы использовать значение, возвращаемое из метода, вызывающий метод может использовать сам метод, где бы было достаточно значение того же типа. Можно также назначить возвращаемое значение переменной. Например, следующие три примера кода выполняют одну и ту же задачу:
int result = obj.AddTwoNumbers(1, 2);
result = obj.SquareANumber(result);
// The result is 9.
Console.WriteLine(result);
result = obj.SquareANumber(obj.AddTwoNumbers(1, 2));
// The result is 9.
Console.WriteLine(result);
result = obj2.DivideTwoNumbers(6,2);
// The result is 3.
Console.WriteLine(result);
Иногда требуется, чтобы метод возвращал более одного значения. Для возврата нескольких значений используются типы кортежей и кортежей. Тип кортежа определяет типы данных элементов кортежа. Кортежные литералы предоставляют фактические значения возвращаемого кортежа. В следующем примере (string, string, string, int) определяет тип кортежа, возвращаемый методом GetPersonalInfo. Выражение (per.FirstName, per.MiddleName, per.LastName, per.Age) является литералом кортежа; Метод возвращает первое, среднее и семейное имя, а также возраст объекта PersonInfo.
public (string, string, string, int) GetPersonalInfo(string id)
{
PersonInfo per = PersonInfo.RetrieveInfoById(id);
return (per.FirstName, per.MiddleName, per.LastName, per.Age);
}
Вызывающий объект затем может использовать возвращенный кортеж с помощью следующего кода:
var person = GetPersonalInfo("111111111");
Console.WriteLine($"{person.Item1} {person.Item3}: age = {person.Item4}");
Имена также могут быть назначены элементам кортежа в определении типа кортежа. В следующем примере показана альтернативная версия метода GetPersonalInfo, использующего именованные элементы:
public (string FName, string MName, string LName, int Age) GetPersonalInfo(string id)
{
PersonInfo per = PersonInfo.RetrieveInfoById(id);
return (per.FirstName, per.MiddleName, per.LastName, per.Age);
}
Затем можно изменить предыдущий вызов метода GetPersonalInfo следующим образом:
var person = GetPersonalInfo("111111111");
Console.WriteLine($"{person.FName} {person.LName}: age = {person.Age}");
Если метод принимает массив в качестве параметра и изменяет значение отдельных элементов, для возврата массива не требуется. C# передает все ссылочные типы по значению, а значение ссылки на массив — указатель на массив.
Элементы с выражением
Обычно используются определения методов, которые возвращаются немедленно с результатом выражения или имеют одну инструкцию в качестве текста метода. Существует сочетание синтаксиса для определения таких методов с помощью =>:
public Point Move(int dx, int dy) => new Point(x + dx, y + dy);
public void Print() => Console.WriteLine(First + " " + Last);
// Works with operators, properties, and indexers too.
public static Complex operator +(Complex a, Complex b) => a.Add(b);
public string Name => First + " " + Last;
public Customer this[long id] => store.LookupCustomer(id);
Если метод возвращает void или является асинхронным методом, текст метода должен быть выражением инструкции (аналогично лямбда-выражениям). Для свойств и индексаторов они должны быть доступны только для чтения, и вы не используете ключевое слово метода доступа get.