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

Страницы работы

Содержание работы

Новые средства языка

В этой главе мы продолжим развитие динамического списка людей с использованием концептуально новых языковых средств, которые появились в С#. Они повышают как выразительность самого языка, так и устойчивость, надежность создаваемых с его помощью программных продуктов. Здесь мы рассмотрим такие специальные структуры данных как: indexer, delegate.

Индексаторы

Новое средство языка — индексатор (indexer) позволяет индексировать любой объект класса так, как если бы он был массивом. Концептуально роль индексатора в языке С++ выполняет операция [], соответствующим образом переопределенная в классе.

Далеко не каждому классу нужна такая функциональность. Например, для объектов класса Man в настоящем его состоянии трудно придумать сколько-нибудь разумное применение индекса. Но если вы задумаете добавить в класс данные об имуществе, которым обладает произвольный человек, то индекс можно будет использовать для выбора отдельных его элементов. Предположим, что вы добавили в класс такое поле:

protected ArrayList goods; // Динамическая коллекция имущества

Теперь индекс для объекта m класса Man будет иметь вполне естественный смысл. Ввод индексатора сделает возможным, например, такое обращение:

object o = m[0];

При этом естественно предположить, что выражение m[0] возвратит нулевой элемент из вложенной в объект m коллекции goods. Вы помните, что каждый объект класса имеет свою собственную копию всех данных класса. Исключение составляют лишь статические члены. Они являются общими для всех объектов класса. Итак, если мы введем в класс Man индексатор, то с его помощью можно будет выбирать любые элементы из коллекции goods, которая является частью класса Man, а следовательно и каждого объекта этого класса.

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

object o = m[-1];

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

Синтаксис индексатора схож с тем, который используется  для реализации свойств, за исключением того, что аксессоры свойств не имеют индекса, а оба аксессора индексатора принимают один и тот же целочисленный параметр. Введите внутрь класса Man индексатор, который имеет два аксессора: get-set.

//======= Объявление и реализация индексатора

public object this [int id]

{

get          // Аксессор выбора

{

      //==== Проверяем, допустим ли индекс?

  if (id < 0 || goods.Count <= id)

  {

    Console.WriteLine ("\nget: wrong id = {0}", id);

    return null;

  }

  else    //==== Возвращаем элемент с допустимым индексом

    return goods[id];

}

set      // Аксессор установки

{

      //==== Проверяем, допустим ли индекс?

  if (0 <= id && id < goods.Count)

    goods[id] = value;

  else

    Console.WriteLine ("\nset: wrong id = {0}",id);

}

}

Обратите внимание на использование ключевого слова this. Именно оно вместе с параметром целого типа (int id) определяет заголовок индексатора. Приведенного кода почти достаточно, чтобы вы могли пользоваться индексатором. Так как в классе Man появилось новое поле данных goods, то надо изменить тела всех конструкторов. В них следует учесть необходимость инициализации коллекции объектов. Кроме того, добавьте еще один конструктор, который позволит мгновенно сделать человека достаточно богатым, инициализируя коллекцию goods массивом объектов произвольной длины.

public Man()     // Default-constructor

{

name = "N/A";   // Имя не определено

age = 0;

goods = new ArrayList();  // Пустая коллекция

}

//==== Старый конструктор с двумя параметрами

public Man (string n, uint a)

{

name = n;

age  = a;

goods = new ArrayList();

}

//==== Полезный конструктор с тремя параметрами

public Man (string n, uint a, object[] junk)

{

name = n;

age  = a;

goods = new ArrayList(junk); // Непустая коллекция

}

Для проверки индексатора осталось изменить тело функции Main. Сначала мы испытаем поведение объекта класса ArrayList в ситуации, когда его индекс принимает недопустимое значение. Затем посмотрим, как будет вести себя в аналогичной ситуации индексатор. Итак, сначала введем код с ошибкой.

static void Main()

{

   //=== Проверим поведение коллекции ArrayList при обращении к ней с недопустимым индексом

ArrayList goods = new ArrayList();

goods[0] = new Object();

}

Запустив, вы получите сообщение о необработанном исключении: System.ArgumentOutOfRangeException. Причина — попытка обратиться к несуществующему элементу коллекции. Это моделирует регулярно встречающуюся ситуацию, когда программист просто забыл вставить в коллекцию хоть один элемент. Ошибку можно легко устранить, вставив (перед обращением к нулевому элементу) строку вида:

goods.Add (new object());

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

try

{

goods[0] = new Object();

}

catch (ArgumentOutOfRangeException e)

Похожие материалы

Информация о работе