Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Podbelsky_V_V_C_Bazovy_kurs.pdf
Скачиваний:
69
Добавлен:
02.06.2015
Размер:
1.73 Mб
Скачать

154

Г л а в а 9

 

 

static void Main()

{

int i = 85, j = 23, k = 56; rank3(ref i, ref j, ref k);

Console.WriteLine(«i={0}, j={1}, k={2}», i, j, k);

}

}

Результат выполнения программы:

i=23, j=56, k=85

Итак, параметры, передаваемые по ссылке, используются для изменения уже существующих значений, внешних по отношению к методу объектов.

Обратите внимание на то, что модификатор ref используется не только перед параметром, но и перед замещающим его аргументом. Особо отметим, что аргументом может быть только переменная (не константа и не выражение) того же типа, что и параметр.

Console.WriteLine(rank3(ref 24, ref 8, ref 4)); //Ошибка! //ERROR - аргументы должны быть переменными!!!

Выходные параметры снабжаются модификатором out и позволяют присвоить значения объектам вызывающего метода даже в тех случаях, когда эти объекты значений еще не имели.

Возникает вопрос: «Зачем нужен модификатор out, если те же действия обеспечивает применение модификатора ref?» Связано появление модификатора out с правилом обязательной инициализации переменных. Модификатор out “говорит” компилятору: «Не обращай внимания на отсутствие значения у переменной, которая использована в качестве аргумента», – и компилятор не выдает сообщения об ошибке. Однако эта переменная обязательно должна получить конкретное значение в теле метода. В противном случае компилятор зафиксирует ошибку.

Метод с выходными параметрами (имеющими модификаторы ref или out) обычно выступает в роли процедуры. Хотя не запрещено такому методу возвращать значение с помощью оператора return. Примером такого метода служит метод TryPase(), который мы уже использовали в примерах программ.

В качестве иллюстрации применения выходных параметров

Методы C#

155

 

 

приведем метод, возвращающий целую (int integer) и дробную (double tra) части вещественного числа (double x). В функции Main() определим три переменных: double real – исходное число, double dPart – дробная часть, int iPart – целая часть. Переменная real инициализирована, переменные iPart, dPart не имеют значений до обращения к методу.

//09_06.cs – параметры-результаты using System;

class Program

{

//Метод возвращает значения целой и дробной частей

//вещественного параметра

static void fun(double x, out int integer, out double fra)

{

integer = (int)x; fra = x integer;

}

static void Main()

{

double real = 53.93; double dPart;

int iPart;

fun(real, out iPart, out dPart); Console.WriteLine("iPart={0}, dPart={1}",iPart,

dPart);

}

}

Результат выполнения программы:

iPart=53, dPart=0,93

Необходимо отметить, что при больших значениях параметра double x величина (int) x может превысить предельное значение типа int. Это приведет к неверным результатам за счет переполнения, о котором мы говорили в главе 3.

9.3. Параметры с типами ссылок

Мы уже достаточно подробно на примерах рассмотрели возможности параметров с типами значений. Отметили существен-

156

Г л а в а 9

 

 

ное различие их передачи по значению и по ссылке. Разобрали два варианта передачи по ссылке – с применением модификатора ref и с использованием модификатора out. Теперь остановимся на особенностях параметров с типами ссылок. Для них также возможна как передача по значению, так и передача по ссылке.

Если параметр с типом ссылки передается методу по значению, то в теле метода создается копия использованного аргумента (копия ссылки). Эта копия аргумента ссылается на какой-то внешний для метода объект, и операторы тела метода через ссылку могут изменить этот внешний объект.

Пример

// 09_07.cs – параметр с типом ссылки using System;

class Program

{

static void sorting(int[] vector) // Упорядочить массив

{

int t;

for (int i = 0; i < vector.Length - 1; i++) for (int j = i+1; j < vector.Length; j++)

if (vector [i] > vector[j])

{ t = vector[i]; vector[i] = vector[j]; vector[j] = t;

}

}

static void arrayPrint(int[] a, string формат) // Вывести вектор

{

int i;

for (i = 0; i < a.Length; i++) Console.Write(формат, a[i]);

}

static void Main() {

int[] array = {1,4,8,2,4,9,3}; arrayPrint(array, "{0,6:d}"); Console.WriteLine(); sorting(array);

Console.WriteLine("Измененный массив:"); arrayPrint(array, "{0,6:d}"); Console.WriteLine();

}

}

Методы C#

157

 

 

Результат выполнения программы:

1 4 8 2 4 9 3

Измененный массив:

1 2 3 4 4 8 9

Метод sorting() в качестве аргумента, передаваемого по значению, должен принимать ссылку на некоторый массив типа int[]. В теле метода выполняется перестановка значений элементов того массива, ссылка на который использована в качестве аргумента. Таким образом, метод изменяет внешний для него объект – одномерный целочисленный массив.

Однако не следует считать, что метод sorting() принимает параметр vector по ссылке. Этот параметр с типом ссылки на одномерный целочисленный массив передае​тся по значению.

Чтобы хорошо понять различие передач по значениям от передач по ссылкам для параметров с типами ссылок, рассмот-рим​ следующий пример. Пусть поставлена задача обменять значения ссылок на два объекта-массива. Сделаем это двумя способами. Определим очень похожие методы (программа 09_08.cs):

static void change1(int[] vec1, int[] vec2)

{

int[] temp; temp = vec1; vec1 = vec2; vec2 = temp;

}

static void change2(ref int[] vec1, ref int[] vec2)

{

int[] temp; temp = vec1; vec1 = vec2; vec2 = temp;

}

Методы отличаются только модификаторами парамет-​ ров. Для change1() параметры передаются по значениям, для change2() – по ссылкам.

static void Main()

{

int[] ar1 = { 1, 4, 8, 2, 4, 9, 3 };

158

Г л а в а 9

 

 

int[] ar2 = { 1, 2, 3 };

change1(ar1, ar2); // Передача по значениям Console.WriteLine("ar1.Length=" + ar1.Length); Console.WriteLine("ar2.Length=" + ar2.Length); change2(ref ar1, ref ar2); // Передача по ссылкам Console.WriteLine("ar1.Length=" + ar1.Length); Console.WriteLine("ar2.Length=" + ar2.Length);

}

Результаты выполнения программы:

ar1.Length=7

ar2.Length=3

ar1.Length=3

ar2.Length=7

В методе Main() определены два конкретных массива:

int[] ar1 = { 1, 4, 8, 2, 4, 9, 3 }; int[] ar2 = { 1, 2, 3 };

Обращение change1(ar1, ar2); не изменяет ссылок, ar1 останется связанной с массивом из 7 элементов, ar2 будет ссылаться, как и ранее, на массив из трех элементов.

При вызове change2(ref ar1, ref ar2); ссылки обменяются значениями – ar1 будет ссылаться на массив из трех элементов, ar2 будет адресовать массив из 7 элементов.

На этом примере мы убедились, что аргумент с типом ссылки (так же как аргумент с типом значения) может изменить свое значение при выполнении тела метода только в том случае, когда он передан методу по ссылке, то есть имеет модификатор ref или out.

Итак, параметр ссылочного типа может быть снабжен модификатором ref. Без него аргумент всегда “смотрит” на свой объект (внешний), может его менять, но не может изменить своего значения. С ref аргумент с типом ссылки может сменить своё значение и “отцепиться” от своего объекта.

В ряде случаев интересно иметь метод, который пригоден не для одного фиксированного типа параметра, а до некоторой степени универсален и допускает подстановку вместо параметра аргументы разных типов. Так как все типы языка C# являются производными от одного базового класса Object, то метод для

Методы C#

159

 

 

обмена значений двух ссылок можно написать так (программа 09_09.cs):

static void change(ref Object ob1, ref Object ob2)

{

Object temp; temp = ob1; ob1 = ob2; ob2 = temp;

}

Чтобы обратиться к этому методу, необходимо привести типы нужных нам аргументов к типу Object. После выполнения метода нужно «вернуть» полученным результатам тип исходных ссылок. В следующем методе проделаны указанные действия:

static void Main()

{

int[] ar1 = { 1, 4, 8, 2, 4, 9, 3 }; int[] ar2 = { 1, 2, 3 };

Object obj1 = ar1, obj2 = ar2;

change(ref obj1, ref obj2); // Передача по ссылкам ar1 = (int []) obj1;

ar2 = (int []) obj2; Console.WriteLine("ar1.Length=" + ar1.Length); Console.WriteLine("ar2.Length=" + ar2.Length);

}

Результаты выполнения программы:

ar1.Length=3

ar2.Length=7

Продемонстрированное в этой программе приведение типов аргументов к типам параметров при передаче по ссылкам обусловлено синтаксисом языка и преследует обеспечение безопасности кода программы.

В случае передачи по значению параметра с типом Object необходимости в явном приведении типа аргумента к типу параметра нет. Вместо параметра с типом Object разрешено подставить аргументы любых типов.

Следующий пример (программа 09_10.cs) иллюстрирует эту возможность. Метод выводит сведения о типе переданного ему аргумента.

160

Г л а в а 9

 

 

static void printType(Object param)

{Console.WriteLine(param.GetType());}

Аргументы при обращениях к методу GetType() могут иметь любой тип, производный от типа Object.

static void Main()

{

int[] ar1 = { 1, 4, 8, 2, 4, 9, 3 }; printType(ar1); printType("строка"); printType(440);

}

}

Результаты выполнения программы:

System.Int32[]

System.String

System.Int32

Предположим, что метод должен определить внутри своего тела некоторый объект и сделать этот объект доступным в точке вызова. Это можно сделать двумя способами.

Во-первых, метод может вернуть ссылку на этот объект как значение, возвращаемое функцией (методом) в точку вызова. Во-вторых, в методе можно использовать параметр с типом ссылки, снабженный модификатором ref либо out. Первый способ иллюстрирует следующая программа.

// 09_11.cs – ссылка как возвращаемое значение

using System; class Program

{

static int[] newAr(uint numb)

{

int [] temp = new int[numb]; for (int i = 0; i < numb; i++) temp[i] = (i + 1) * (i + 1);

return temp;

}

static void Main()

{

int[] vector = newAr(6);

Методы C#

161

 

 

foreach (int el in vector) Console.Write(el+" ");

Console.WriteLine();

}

}

Результаты выполнения программы:

1 4 9 16 25 36

В программе метод (функция) newAr(), получив в качестве аргумента целое неотрицательное значение, создает одномерный целочисленный массив, присваивает его элементам значения квадратов натуральных чисел и возвращает в точку вызова ссылку на сформированный массив. В методе Main() определена переменная-ссылка vector на целочисленный массив и ей присвоен результат вызова метода newAr(). Результаты выполнения программы иллюстрируют сказанное.

9.4. Методы с переменным числом аргументов

Упараметра с модификатором params назначение особое

он представляет в методе список аргументов неопределенной (заранее не фиксированной) длины. Его тип – одномерный массив с элементами типа, указанного в спецификации параметра.

Как уже сказано, этот параметр может быть только последним (или единственным) в списке параметров. В обращении к методу этот параметр заменяется списком аргументов, каждый из которых должен иметь тот же тип, что и элементы массива параметров. В теле метода отдельные реально использованные аргументы представлены элементами массива-параметра. Количество аргументов соответствует длине массива.

В качестве примера приведем программу с методом для вычисления значений полинома Pn(x)=a0*xn+a1*xn-1+...+an-1*x+an:

// 09_12.cs – массив-параметр using System;

class Program

{

162

Г л а в а 9

 

 

// Вычисляет значение полинома c целыми коэффициентами:

static double polynom(double x, params int [] coef)

{

double result = 0.0;

for (int i = 0; i < coef.Length; i++) result = result * x + coef[i];

return result;

}

static void Main()

{

Console.WriteLine(polynom(3.0, 3, 1, 2));

}

}

Результат выполнения программы:

32

Метод polynom() возвращает значение типа double, т. е. играет роль функции. Первый параметр double x представляет значение аргумента полинома. Второй параметр params int [] coef дает возможность передать в метод список коэффициентов полинома a0, a1. ..an-1, an. Все коэффициенты должны иметь целочисленные значения. Их реальное количество, т. е. степень полинома, при обращении определяется числом использованных аргументов, а в теле метода – значением поля coef.Length. В теле метода:

coef[0]=a0, coef[1]=a1, ..., coef[n]=an.

Для вычисления полинома использована схема Горнера, позволяющая заменить явные возведения в степень последовательными умножениями:

Pn(x)=(...(a0*x+a1)*x+a2)*x+...+an-1)*x+an

В том случае, когда требуется метод, принимающий произвольное число параметров любых разных типов, Дж. Рихтер [10] предлагает использовать параметр вида params Object[ ]. Он приводит в качестве примера метод, который выводит обозначения (наименования) типов всех переданных методу аргументов. Вот программа с этим методом:

// 09_13.cs массив-параметр «универсального» типа using System;

Методы C#

163

 

 

class Program {

// Метод выводит названия типов аргументов: static void DisplayTypes(params Object[] objects) {

foreach (Object o in objects) Console.WriteLine(o.GetType());

}

static void Main() {

DisplayTypes("yes", 432, new Object());

}

}

Результат выполнения программы:

System.String

System.Int32

System.Object

Обратим внимание, что параметр, снабженный модификатором params, обеспечивает передачу аргументов по значению, т.е. значения аргументов после выполнения метода не изменяются. Следующая программа иллюстрирует это правило.

// 09_14.cs переменное число аргументов using System;

class Program

{

static void varParams(params int[] var) { for (int i = 0; i < var.Length; i++) {

var[i] *= 2;

Console.Write("var[{0}]={1} ", i, var[i]);

}

}

static void Main() {

int a = 2, b = 3, c = 5; varParams(a, b, c);

Console.WriteLine("\na={0}, b={1}, c={2}", a, b, c);

}

}

Результат выполнения программы:

var[0]=4 var[1]=6 var[2]=10 a=2, b=3, c=5

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]