Категории
Самые читаемые
onlinekniga.com » Компьютеры и Интернет » Программирование » ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание - Эндрю Троелсен

ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание - Эндрю Троелсен

Читать онлайн ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание - Эндрю Троелсен

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 75 76 77 78 79 80 81 82 83 ... 259
Перейти на страницу:

 }

 static void ComputationFinishedHandler(string msg) { Console.WriteLine(msg); }

}

Но можно зарегистрировать программу обработки событий для конкретного события и так, как показано ниже (в остальном программный код изменений не претерпевает).

m.ComputationFinished += ComputationFinishedHandler;

Обратите внимание на то, что мы не создаем непосредственно соответствующий тип делегата, а просто указываем метод, который соответствует ожидаемой сигнатуре делегата (в данном случае это метод, не возвращающий ничего и получающий один объект типа System.String). Ясно, что компилятор C# при этом должен обеспечить типовую безопасность. Если метод ComputationFinishedHandler() не получает System.String и не возвращает void, то вы получите сообщение об ошибке компиляции.

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

// .NET 2.0 допускает преобразование обработчиков событий

// в соответствующие делегаты.

SimpleMath.MathMessage mmDelegate = (SimpleMath.MathMessage)ComputationFinishedHandler;

Console.WriteLine(mmDelegate.Method);

Если выполнить этот программный код, то заключительный оператор Console.WriteLine() напечатает сигнатуру ComputationFinishedHandler, как показано на рис. 8.9.

Рис. 8.9. Можно извлечь делегат из соответствующего обработчика события

Исходный код. Проект AnonymousMethods размещен в подкаталоге, соответствующем главе 8.

Резюме

В этой главе был рассмотрен ряд подходов, позволяющих реализовать возможность двухстороннего взаимодействия объектов. Сначала было рассмотрено использование интерфейсов обратного вызова, которые обеспечивают возможность для объекта А вызывать объект В с помощью общего интерфейса. Этот подход не является специфическим для .NET, а может использоваться в любых языках и на любых платформах, допускающих программирование на основе интерфейсов.

Затем было рассмотрено ключевое слово C# delegate, которое используется для непрямого построения классов, производных от System.MulticastDelegate. Как выяснилось, делегат представляет собой объект, хранящий список методов, доступных для вызова. При этом вызовы, могут быть синхронными (они выполняются с помощью метода Invoke()) или асинхронными (они выполняются с помощью методов BeginInvoke() и EndInvoke()). Асинхронная природа типов делегата .NET будет рассмотрена позже.

Ключевое слово C# event при использовании с типом делегата позволяет упростить процесс отправки сообщений событий вызывающим объектам. Как показывает генерируемый CIL-код, модель событий .NET сводит ситуацию к скрытым вызовам типов System.Delegate/System.MulticastDelegate. В этой связи ключевое слово C# event оказывается необязательным и просто экономит время при наборе текста программы.

Новая возможность, появившаяся в C# 2005 и получившая название анонимных методов, позволяет непосредственно ассоциировать с событием (неименованный) блок операторов программного кода. Анонимные методы могут игнорировать параметры, посылаемые событием, и получать доступ в "внешним переменным" определяющего метода. В завершение главы был рассмотрен упрощенный способ регистрации событий с помощью группового преобразования методов.

ГЛАВА 9. Специальные приемы построения типов

В этой главе вы расширите горизонты вашего понимания языка C#, рассмотрев ряд более сложных (но весьма полезных) синтаксических конструкций. Сначала мы с вами выясним, как использовать метод индексатора. Этот механизм в C# позволяет строить пользовательские типы, обеспечивающие доступ к внутренним подтипам на основе синтаксиса массивов. Научившись строить методы индексатора, вы затем узнаете, как перегружать различные операции (+, -, ‹, › и т.д.) и явно или неявно создавать пользовательские подпрограммы преобразования типов (а также узнаете, зачем это может понадобиться).

Во второй половине главы будет рассмотрен небольшой набор ключевых слов C#, которые позволяют реализовать весьма интересные конструкции, хотя используются не очень часто. Вы узнаете о том, как с помощью ключевых слов checked и unchecked программно учитывать условия переполнения и потери значимости, а также о том, как создается "небезопасный" программный контекст, обеспечивающий возможность непосредственного управления ссылочными типами в C#. Завершается глава обсуждением роли директив препроцессора C#.

Создание пользовательских индексаторов

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

// Объявление массива целых значений.

int[] myInts = {10, 9, 100, 432, 9874};

// Использование операции [] для доступа к элементам.

for (int j = 0; j ‹ myInts.Length; j++) Console.WriteLine("Индекс {0} = {1}", j, myInts[j]);

Этот программный код ни в коем случае не претендует на новизну. Но язык C# дает возможность строить пользовательские классы и структуры, которые могут индексироваться подобно стандартным массивам. Поэтому совсем не удивительно, что метод, который обеспечивает такой доступ к элементам, называется индекса-mopoм.

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

// Индексаторы обеспечивают доступ к элементам подобно массивам.

public class Program {

 static void Main(string[] args) {

  Console.WriteLine("***** Забавы с индексаторами *****n");

  // Предположим, что Garage имеет метод индексатора.

  Garage carLot = new Garage();

  // Добавление в гараж машин с помощью индексатора.

  сarLot[0] = new Саr("FееFee", 200);

  carLot[1] = new Car("Clunker", 90);

  carLot[2] = new Car("Zippy", 30);

  // Чтение и отображение элементов с помощью индексатора.

  for (int i = 0; i ‹ 3; i++) {

   Console.WriteLine("Hомep машины: {0}", i);

   Console.WriteLite("Нaзвaниe: {0}", carLot[i].PetName);

   Console.WriteLine("Максимальная скорость: {0}", carLot[i].CurrSpeed);

   Console.WriteLine();

  }

  Console.ReadLine();

 }

}

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

Здесь возникает вопрос: "Как сконфигурировать класс (или структуру), чтобы обеспечить поддержку соответствующих функциональных возможностей?" Индексатор в C# представляет собой несколько "искаженное" свойство. Для создания индексатора в самой простой форме используется синтаксис this[]. Вот как может выглядеть подходящая модификации типа Garage.

// Добавление индексатора в определение класса.

public class Garage: IEnumerable { // для каждого элемента

 …

 // Использование ArrayList для типов Car.

 private ArrayList carArray = new ArrayList();

 // Индексатор возвращает тип Car, соответствующий

 // Числовому индексу.

 public Car this[int pos] {

  // ArrayList тоже имеет индексатор!

  get { return (Car)carArray[pos]; }

  set { carArray.Add(value); }

 }

}

Если не обращать внимания на ключевое слово this, то объявление индексатора очень похоже на объявление свойства в C#. Но следует подчеркнуть, что индексаторы не обеспечивают иных функциональных возможностей массива, кроме возможности использования операции индексирования, Другими словами, пользователь объекта не может применить программный код, подобный следующему.

// Используется свойство ArrayList.Count? Нет!

Console.WriteLine("Машин в наличии: {0} ", carLot.Count);

Для поддержки этой функциональной возможности вы должны добавить свое свойство Count в тип Garage и, соответственно, делегат.

public class Garage: IEnumerable {

 …

 // Локализация/делегирование в действии снова.

 public int Count { get { return carArray.Count; } }

}

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

1 ... 75 76 77 78 79 80 81 82 83 ... 259
Перейти на страницу:
На этой странице вы можете бесплатно читать книгу ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание - Эндрю Троелсен.
Комментарии