| Инкапсуляция |
Классы: основные понятия
Параметры методов
При вызове метода выполняются следующие действия:
- Вычисляются выражения, стоящие на месте аргументов.
- Выделяется память под параметры метода в соответствии с их типом.
- Каждому из параметров сопоставляется соответствующий аргумент (аргументы как бы накладываются на параметры и замещают их).
- Выполняется тело метода.
- Если метод возвращает значение, оно передается в точку вызова; если метод имеет тип void, управление передается на оператор, следующий после вызова.
При этом проверяется соответствие типов аргументов и параметров и при необходимости выполняется их преобразование. При несоответствии типов выдается диагностическое сообщение. Листинг 5.3 иллюстрирует этот процесс.
using System;
namespace ConsoleApplication1
{ class Class1
{
static int Max(int a, int b) // метод выбора максимального значения
{
if ( a > b ) return a;
else return b;
}
static void Main()
{
int a = 2, b = 4;
int x = Max( a, b ); // вызов метода Max
Console.WriteLine( x ); // результат: 4
short t1 = 3, t2 = 4;
int y = Max( t1, t2 ); // вызов метода Max
Console.WriteLine( y ); // результат: 4
int z = Max( a + t1, t1 / 2 * b ); // вызов метода Max
Console.WriteLine( z ); // результат: 5
}
}
}
Листинг
5.3.
Передача параметров методу
Внимание
Главное требование при передаче параметров состоит в том, что аргументы при вызове метода должны записываться в том же порядке, что и в заголовке метода, и должно существовать неявное преобразование типа каждого аргумента к типу соответствующего параметра. Количество аргументов должно соответствовать количеству параметров. Иллюстрация приведена на рис. 5.4.
Существуют два способа передачи параметров: по значению и по ссылке.
При передаче по значению метод получает копии значений аргументов, и операторы метода работают с этими копиями. Доступа к исходным значениям аргументов у метода нет, а, следовательно, нет и возможности их изменить.
При передаче по ссылке ( по адресу ) метод получает копии адресов аргументов, он осуществляет доступ к ячейкам памяти по этим адресам и может изменять исходные значения аргументов, модифицируя параметры.
В C# для обмена данными между вызывающей и вызываемой функциями предусмотрено четыре типа параметров:
- параметры-значения;
- параметры-ссылки — описываются с помощью ключевого слова ref ;
- выходные параметры — описываются с помощью ключевого слова out ;
- параметры-массивы — описываются с помощью ключевого слова params.
Ключевое слово предшествует описанию типа параметра. Если оно опущено, параметр считается параметром-значением. Параметр-массив может быть только один и должен располагаться последним в списке, например:
public int Calculate( int a, ref int b, out int c, params int[] d ) …
Параметры-значения
Параметр-значение описывается в заголовке метода следующим образом:
тип имя
Пример заголовка метода, имеющего один параметр-значение целого типа:
void P( int x )
Имя параметра может быть произвольным. Параметр х представляет собой локальную переменную, которая получает свое значение из вызывающей функции при вызове метода. В метод передается копия значения аргумента.
Механизм передачи следующий: из ячейки памяти, в которой хранится переменная, передаваемая в метод, берется ее значение и копируется в специальную область памяти — область параметров. Метод работает с этой копией. По завершении работы метода область параметров освобождается. Этот способ годится только для передачи в метод исходных данных.
При вызове метода на месте параметра, передаваемого по значению, может находиться выражение, для типа которого существует неявное преобразование типа выражения к типу параметра.
Например, пусть в вызывающей функции описаны переменные и им до вызова метода присвоены значения:
int x = 1; sbyte c = 1; ushort y = 1;
Тогда следующие вызовы метода Р, заголовок которого был описан ранее, будут синтаксически правильными:
P( x ); P( c ); P( y ); P( 200 ); P( x / 4 + 1 );
Параметры-ссылки
Признаком параметра-ссылки является ключевое слово ref перед описанием параметра:
ref тип имя
Пример заголовка метода, имеющего один параметр-ссылку целого типа:
void P( ref int x )
При вызове метода в область параметров копируется адрес аргумента, и метод через него имеет доступ к ячейке, в которой хранится аргумент. Метод работает непосредственно с переменной из вызывающей функции и, следовательно, может ее изменить, поэтому если в методе требуется изменить значения параметров, они должны передаваться только по ссылке.
Внимание
При вызове метода на месте параметра-ссылки может находиться только ссылка на инициализированную переменную точно того же типа. Перед именем параметра указывается ключевое слово ref.
Проиллюстрируем передачу параметров-значений и параметров-ссылок на примере (листинг 5.4).
using System;
namespace ConsoleApplication1
{ class Class1
{
static void P( int a, ref int b )
{
a = 44; b = 33;
Console.WriteLine( "внутри метода {0} {1}", a, b );
}
static void Main()
{
int a = 2, b = 4;
Console.WriteLine( "до вызова {0} {1}", a, b );
P( a, ref b );
Console.WriteLine( "после вызова {0} {1}", a, b );
}
}
}
Листинг
5.4.
Параметры-значения и параметры-ссылки
Результаты работы этой программы:
до вызова 2 4 внутри метода 44 33 после вызова 2 33
Несколько иная картина получится, если передавать в метод не величины значимых типов, а экземпляры классов, то есть величины ссылочных типов. Для простоты можно считать, что объекты всегда передаются по ссылке.
Выходные параметры
Довольно часто возникает необходимость в методах, которые формируют несколько величин. В этом случае становится неудобным ограничение параметров-ссылок: необходимость присваивания значения аргументу до вызова метода. Это ограничение снимает спецификатор out. Параметру, имеющему этот спецификатор, должно быть обязательно присвоено значение внутри метода.
Изменим описание второго параметра в листинге 5.4 так, чтобы он стал выходным (листинг 5.5).
using System;
namespace ConsoleApplication1
{ class Class1
{
static void P( int a, out int b )
{
a = 44; b = 33;
Console.WriteLine( "внутри метода {0} {1}", a, b );
}
static void Main()
{
int a = 2, b;
P( a, out b );
Console.WriteLine( "после вызова {0} {1}", a, b );
}
}
}
Листинг
5.5.
Выходные параметры
При вызове метода перед соответствующим параметром тоже указывается ключевое слово out.
