Новые средства языка. Индексаторы. Делегат на основе обычного метода класса. Делегаты на основе класса MulticastDelegate, страница 2

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

try

{

goods[0] = new Object();

}

catch (ArgumentOutOfRangeException e)

{

Console.WriteLine ("\nI got the exception:\n" + e.Message);

}

Не поленитесь и проверьте работу этого фрагмента (при пустой коллекции). Приятным моментом здесь является та обстоятельность, с которой объект e выводит причину ошибки. Несмотря на это приятное открытие, подобный подход к разработке программ способен привести в уныние любого программиста. Хотя, за всех не скажешь.

Теперь посмотрим, как в аналогичной ситуации поведет себя индексатор. Для этого вновь измените тело функции Main. В ней мы сначала поработаем со списком без явных ошибок, а затем специально обратимся к нему с недопустимыми значениями индекса.

static void Main()

{

object[] goods = { "Bike", "Wife", "Summer House" };

Man alex = new Man ("Alex Black", 56, goods);   // Создадим богатого человека

object o = alex[0];    // Проверим поведение аксессора get

Console.WriteLine(o);

Console.WriteLine("Alex has: {0}, {1}, {2}", alex[0], alex[1], alex[2]);

alex[0] = "New Bike";    // Проверим поведение аксессора set

Console.WriteLine (alex[0]);    // и снова get

}

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

alex[0] = Bike

Alex has: Bike, Wife, Summer House

alex[0] = New Bike

Теперь проверим поведение индексатора в случае некорректного обращения с индексом. Добавьте в конец функции Main такие строки.

//==== Проверим поведение средств защиты

Console.Write (alex [-1]);// get

alex [2048] = o;           // set

Хорошо то, что приложение не прерывается и реагирует на ошибки вполне адекватным образом. В окне вы видите ту реакцию, которая было заложена нами при создании индексатора.

get: wrong id = -1

set: wrong id = 2048

В заключение обзора возможностей индексатора, отметим, что применение индексатора, например, для класса «многоугольник» было бы более естественным, чем для объектов класса Man. С помощью индексатора мы могли бы простым образом опрашивать или устанавливать координаты произвольной вершины многоугольника.

Синтаксис индексатора напоминает синтаксис свойств, но запомните. Свойство может быть как обычным членом класса, так и статическим. Индексатор же статическим быть не может.


Делегаты

Делегаты имеют известный нам аналог в языке С++. Им является указатель на функцию. Вспомним, как это делается. Имя функции в C++ — это адрес ее начала (адрес точки входа). При работе с указателем на функцию мы сначала должны его объявить. Это можно сделать так:

double (*pTrig) (double) = sin;

cout << "sin(1.57) = " << pTrig(1.57)<<"\n\n";

Указатель pTrig объявлен, инициализирован адресом функции sin и использован для ее вызова pTrig(1.57). Далее этот же указатель может быть использован для вызова другой функции, но с той же сигнатурой. Например:

pTrig = tan;

cout << "tan (0.78) = " << pTrig (0.78)<<"\n\n";

Если мы имеем свою собственную функцию Sum с сигнатурой, соответствующей указателю, то и ее можно вызвать с помощью pTrig, например:

double Sum (double eps)

{

double sum = 1.;

for (int n=2, s=1;  fabs(1./n) > eps;  n++, s=-s)

sum += double(s)/n;

return sum;

}

Иллюстрируем все вызовы в одном месте.

void main()

{

double (*pTrig) (double) = sin;

cout << "sin (1.57) = " << pTrig(1.57)<<"\n\n";

pTrig = tan;

cout << "tan (0.78) = " << pTrig (0.78)<<"\n\n";

pTrig = Sum;

cout << "Sum (1e-5) = " << pTrig (1e-5)<<"\n\n";