Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЛР №5.doc
Скачиваний:
1
Добавлен:
01.03.2025
Размер:
387.58 Кб
Скачать

Вложенные типы

В классе можно определять типы данных, внутренние по отношению к классу. Так определяются вспомогательные типы, которые используются только содержащим их классом. Механизм вложенных типов позволяет скрыть ненужные детали и более полно реализовать принцип инкапсуляции. Непосредственный доступ извне к такому классу невозможен (имеется в виду доступ по имени без уточнения). Для вложенных типов можно использовать те же спецификаторы, что и для полей класса.

Например, введем в наш класс Monster вспомогательный класс Gun. Объекты этого класса без "хозяина" бесполезны, поэтому его можно определить как внутренний:

using System;

namespace ConsoleApplication1

{ class Monster

{

class Gun

{

...

}

...

}

}

Перегрузка методов

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

Компилятор определяет, какой именно метод требуется вызвать, по типу фактических параметров. Этот процесс называется разрешением (resolution) перегрузки. Тип возвращаемого методом значения в разрешении не участвует. Механизм разрешения основан на достаточно сложном наборе правил, смысл которых сводится к тому, чтобы использовать метод с наиболее подходящими аргументами и выдать сообщение, если такой не найдется. Допустим, имеется четыре варианта метода, определяющего наибольшее значение:

// Возвращает наибольшее из двух целых:

int max( int a, int b )

// Возвращает наибольшее из трех целых:

int max( int a, int b, int c )

// Возвращает наибольшее из первого параметра и длины второго:

int max ( int a, string b )

// Возвращает наибольшее из второго параметра и длины первого:

int max ( string b, int a )

...

Console.WriteLine( max( 1, 2 ) );

Console.WriteLine( max( 1, 2, 3 ) );

Console.WriteLine( max( 1, "2" ) );

Console.WriteLine( max( "1", 2 ) );

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

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

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

int max( int a, int b )

int max( int a, ref int b )

Перегрузка широко используется в классах библиотеки .NET. Например, в стандартном классе Console метод WriteLine перегружен 19 раз для вывода величин разных типов.

Рекурсивные методы

Рекурсивным называется метод, который вызывает сам себя. В этом случае рекурсию считают прямой. Косвенной называют рекурсию, в которой два или более метода вызывают друг друга. Если метод вызывает себя, в стеке создается копия значений его параметров, как и при вызове обычного метода, после чего управление передается первому исполняемому оператору метода. При повторном вызове этот процесс повторяется.

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

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

К достоинствам рекурсии можно отнести компактность записи, к недостаткам – расход времени и памяти на повторные вызовы метода и передачу ему копий параметров, а главное, опасность переполнения стека.

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

Иногда бывает удобно создать метод, в который можно передавать разное количество аргументов. Язык C# предоставляет такую возможность с помощью ключевого слова params. Параметр, помеченный этим ключевым словом, размещается в списке параметров последним и обозначает массив заданного типа неопределенной длины, например:

public int Calculate( int a, out int c, params int[] d ) …

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

using System;

namespace ConsoleApplication1

{ class Class1

{

public static double Average( params int[] a )

{

if ( a.Length == 0 )

throw new Exception( "Недостаточно аргументов в методе" );

double av = 0;

foreach ( int elem in a ) av += elem;

return av / a.Length;

}

static void Main()

{ try

{

int[] a = { 10, 20, 30 };

Console.WriteLine( Average( a ) ); // 1

int[] b = { -11, -4, 12, 14, 32, -1, 28 };

Console.WriteLine( Average( b ) ); // 2

short z = 1, e = 12;

byte v = 100;

Console.WriteLine( Average( z, e, v ) ); // 3

Console.WriteLine( Average() ); // 4

}

catch( Exception e )

{

Console.WriteLine( e.Message );

return;

}

}

}

}

Результат работы программы:

10

20

40

Недостаточно аргументов в методе

Параметр-массив может быть только один и должен располагаться последним в списке. Соответствующие ему аргументы должны иметь типы, для которых возможно неявное преобразование к типу массива.