
- •Глава 9. Методы с#
- •9.1. Методы-процедуры и методы-функции
- •9.2. Соотношение фиксированных параметров и аргументов
- •9.3. Параметры с типами ссылок
- •1 4 8 2 4 9 3
- •1 2 3 4 4 8 9
- •1 4 9 16 25 36
- •9.4. Методы с переменным числом аргументов
- •9.5. Перегрузка методов
- •9.6. Рекурсивные методы
- •9.7. Применение метода Array.Sort()
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();
}
Результаты выполнения программы: