Скачиваний:
74
Добавлен:
24.03.2015
Размер:
300.54 Кб
Скачать

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

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

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

// 09_07.cs - параметр с типом ссылки

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 Main09_07()

{

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();

}

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

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 Main09_08()

{

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

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[] аr2 = { 1, 2, 3 };

Обращение changel(ar1, аr2); не изменяет ссылок, ar1 останется связанной с массивом из 7-ми элементов, аг2 будет ссылаться как и ранее на массив из трёх элементов.

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

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

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

В ряде случаев интересно иметь метод, который пригоден не для одного фиксированного типа параметра, а до некоторой степени универсален и допускает подстановку вместо параметра аргументы разных типов. Так как все типы языка С# являются производными от одного базового класса Object, то метод для обмена значений двух ссылок можно написать так (программа 09_09.cs):

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

{

Object temp;

temp = obi;

obi = 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) иллюстрирует эту возможность. Метод выводит сведения о типе переданного ему аргумента.

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 - ссылка как возвращаемое значение

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);

foreach (int el in vector)

Console.Write(el + " ");

Console.WriteLine();

}

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

Соседние файлы в папке Lekc_C#