- •Сборки (assembly) в среде .Net. Проблема версионности сборок и ее решение.
- •Номер версии в .Net
- •Сведения о версии
- •Номер версии сборки
- •Информационная версия сборки
- •Общая система типов данных в среде .Net. Размерные и ссылочные типы данных. Типы, переменные и значения
- •Пользовательские типы
- •Система общих типов cts
- •Ссылочные типы
- •Типы литеральных значений
- •Неявные типы, анонимные типы и типы, допускающие значение null
- •Упаковка и распаковка размерных типов данных в среде .Net.
- •Производительность
- •Упаковка–преобразование
- •Распаковка-преобразование
- •Ссылочные типы данных. Объектная модель в среде .Net и языке c#.
- •Модели ручной и автоматической утилизации динамической памяти, их сравнительная характеристика. Модель с ручным освобождением памяти
- •Модель с автоматической «сборкой мусора»
- •Модель автоматической утилизации динамической памяти, основанная на сборке мусора. Проблема недетерминизма.
- •Модель автоматической утилизации динамической памяти, основанная на аппаратной поддержке (тегированной памяти).
- •Сборка мусора в среде .Net. Построение графа достижимых объектов.
- •Сборка мусора в среде .Net. Механизм поколений объектов.
- •Модель детерминированного освобождения ресурсов в среде .Net. Интерфейс iDisposable и его совместное использование с завершителем (методом Finalize).
- •«Мягкие ссылки» и кэширование данных в среде .Net.
- •Краткие и длинные слабые ссылки
- •Краткая ссылка
- •Длинная ссылка
- •Правила использования слабых ссылок
- •Динамические массивы в среде .Net и языке c#.
- •Приведение типов в массивах
- •Все массивы неявно реализуют /Enumerable, /Collection и iList
- •Передача и возврат массивов
- •Создание массивов с ненулевой нижней границей
- •Производительность доступа к массиву
- •Небезопасный доступ к массивам и массивы фиксированного размера
- •Делегаты в среде .Net и механизм их работы. Знакомство с делегатами
- •Использование делегатов для обратного вызова статических методов
- •Использование делегатов для обратного вызова экземплярных методов
- •Правда о делегатах
- •Использование делегатов для обратного вызова множественных методов (цепочки делегатов)
- •Поддержка цепочек делегатов в с#
- •Расширенное управление цепочкой делегатов
- •Упрощение синтаксиса работы с делегатами в с#
- •Упрощенный синтаксис № 1: не нужно создавать объект-делегат
- •Упрощенный синтаксис № 2: не нужно определять метод обратного вызова
- •Упрощенный синтаксис № 3: не нужно определять параметры метода обратного вызова
- •Упрощенный синтаксис № 4: не нужно вручную создавать обертку локальных переменных класса для передачи их в метод обратного вызова
- •Делегаты и отражение
- •События в среде .Net; реализация событий посредством делегатов. События
- •Этап 1: определение типа, который будет хранить всю дополнительную информацию, передаваемую получателям уведомления о событии
- •Этап 2: определение члена-события
- •Этап 3: определение метода, ответственного за уведомление зарегистрированных объектов о событии
- •Этап 4: определение метода, транслирующего входную информацию в желаемое событие
- •Как реализуются события
- •Создание типа, отслеживающего событие
- •События и безопасность потоков
- •Явное управление регистрацией событий
- •Конструирование типа с множеством событий
- •Исключительные ситуации и реакция на них в среде .Net. Достоинства
- •Механика обработки исключений
- •Блок try
- •Блок catch
- •Блок finally
- •Генерация исключений
- •Определение собственных классов исключений
- •Исключения в платформе .Net Framework
- •Исключения и традиционные методы обработки ошибок
- •Управление исключениями средой выполнения
- •Фильтрация исключений среды выполнения
- •21 Средства многопоточного программирования в среде .Net. Автономные потоки. Пул потоков.
- •Создание и использование потоков
- •Запуск и остановка потоков
- •Методы управления потоками
- •Безопасные точки
- •Свойства потока
- •Потоки Windows в clr
- •К вопросу об эффективном использовании потоков
- •Пул потоков в clr
- •Ограничение числа потоков в пуле
- •22. Асинхронные операции в среде .Net. Асинхронный вызов делегатов.
- •23. Синхронизация программных потоков в среде .Net. Блокировки.
- •Двойная блокировка
- •Класс ReaderWriterLock
- •Использование объектов ядра Windows в управляемом коде
- •Вызов метода при освобождении одного объекта ядра
- •24. Синхронизация программных потоков в среде .Net. Атомарные (Interlocked-операции). Семейство lnterlocked-методов
- •25. Прерывание программных потоков в среде .Net. Особенности исключительной ситуации класса ThreadAbortException.
- •26. Мониторы в среде .Net. Ожидание выполнения условий с помощью методов Wait и Pulse. Класс Monitor и блоки синхронизации
- •«Отличная» идея
- •Реализация «отличной» идеи
- •Использование класса Monitor для управления блоком синхронизации
- •Способ синхронизации, предлагаемый Microsoft
- •Упрощение кода c# при помощи оператора lock
- •Способ синхронизации статических членов, предлагаемый Microsoft
- •Почему же «отличная» идея оказалась такой неудачной
- •Целостность памяти, временный доступ к памяти и volatile-поля
- •Временная запись и чтение
- •Поддержка volatile-полей в с#
- •27. Асинхронный вызов делегатов.
- •Общие типы (Generics)
- •Инфраструктура обобщений
- •Открытые и закрытые типы
- •Обобщенные типы и наследование
- •Проблемы с идентификацией и тождеством обобщенных типов
- •«Распухание» кода
- •Обобщенные интерфейсы
- •Обобщенные делегаты
- •Обобщенные методы
- •Логический вывод обобщенных методов и типов
- •Обобщения и другие члены
- •Верификация и ограничения
- •Основные ограничения
- •Дополнительные ограничения
- •Ограничения конструктора
- •Другие вопросы верификации
- •Приведение переменной обобщенного типа
- •Присвоение переменной обобщенного типа значения по умолчанию
- •Сравнение переменной обобщенного типа с null
- •Сравнение двух переменных обобщенного типа
- •Использование переменных обобщенного типа в качестве операндов
- •Преимущества использования общих типов
- •29. Итераторы в среде .Net. Создание и использование итераторов.
- •Общие сведения о итераторах
Использование делегатов для обратного вызова экземплярных методов
Делегаты позволяют вызывать экземплярные методы объекта. Чтобы понять, как работает обратный вызов экземплярного метода, взгляните на метод InstanceDelegateDemo.
Обратите внимание, что объект pтипа Program создается в методе InstanceDelegateDemo. У объекта Program нет экземплярных полей или свойств — я создал его просто для наглядности.
Когда при вызове Counter создается новый объект-делегат Feedback, его конструктору передается p.FeedbackToFile. Это заставляет делегат стать оболочкой для ссылки на метод FeedbackToFile, который является экземплярным, а не статическим методом.
Когда Counter обращается к методу обратного вызова, указанному в параметре fb, вызывается экземплярный метод FeedbackToFile, а адрес только что созданного объектаpпередается как явный параметр в метод FeedbackToFile.
Этот пример призван показать, что делегаты могут служить оболочкой как для экземплярных, так и для статических методов.
В случае экземплярного метода, делегат должен знать экземпляр объекта, который собирается обрабатывать вызываемый им метод.Создание оболочек экземплярных методов полезно, так как позволяет коду внутреннего объекта получить доступ к экземплярным членам объекта. Это означает, что у объекта может быть состояние, которое может использоваться во время работы метода обратного вызова.
Правда о делегатах
Может показаться, что использовать делегаты несложно: вы определяете делегат ключевым словом delegate языка C#, создаете его экземпляр с помощью знакомого оператора new и вызываете метод обратного вызова, пользуясь известным вам синтаксисом «вызова метода» (в котором вместо имени метода указывается переменная, ссылающаяся на объект-делегат).
Но на самом деле делегаты гораздо сложнее, чем показывают продемонстрированные примеры.
Давайте-ка еще раз изучим эту строку кода:
internal delegate void Feedback(Int32 value);
На самом деле, обнаружив такую строку, компилятор создает полное определение класса, которое выглядит примерно так:
internal class Feedback : System.MulticastDelegate
{
// Конструктор.
public Feedback(Object obj, IntPtr method) {}
// Метод, прототип которого задан в исходном тексте.
public virtual void Invoke(Int32 value){}
// Методы, обеспечивающие асинхронный обратный вызов.
public virtual IAsyncResult BeginInvoke(Int32 value, AsyncCallback callback, Object obj){}
public virtual void EndInvoke(IAsyncResult result){}
}
У определенного компилятором класса четыре метода:
конструктор
Invoke
Beginlnvoke
Endlnvoke
В этом примере компилятор создал класс Feedback, производный от типа SystemMulticastDelegate, определенного в библиотеке классов .NET Framework Class Library.
Внимание!Класс System.MulticastDelegate является производным от System.Delegate, который, в свою очередь, наследует класс System.Object.
Наличие двух классов делегатов — факт неприятный, но так уж сложилось исторически; в FCL должен быть лишь один класс делегата. К сожалению, нужно помнить об обоих классах, потому что, даже если вы будете в качестве типов делегатов выбирать в качестве базового класс MulticastDelegate, иногда придется работать с типами делегатов, используя методы, определенные в классе Delegate, а не MulticastDelegate. В частности, у класса Delegate есть статические методы Combine и Remove.
Сигнатуры обоих методов указывают, что они принимают параметры типа Delegate. Так как ваши типы делегатов являются производными от MulticastDelegate, который наследует Delegate, экземпляры вашего типа делегата можно передавать в эти методы.
Feedback закрытый класс, так как делегат был объявлен в исходном тексте как internal. Если бы он был объявлен в исходном тексте с модификатором public, то сгенерированный компилятором класс Feedback был бы соответственно открытым.
Надо иметь в виду, что типы делегатов могут быть определены как внутри класса (вложенные внутрь другого типа), так и с глобальной областью действия. Поскольку делегаты — это классы, их можно определять в любом месте, где можно определить класс.
Поскольку все типы делегатов являются потомками MulticastDelegate, они наследуют все поля, свойства и методы MulticastDelegate. Наверное, самые важные из них — это три закрытых поля (табл. 15-1).
Табл. 15-1. Важнейшие закрытые поля MulticastDelegate
Поле |
Тип |
Описание |
_target
|
System.Object
|
Когда объект-делегат служит оберткой статического метода, это поле содержит null. Когда объект-делегат служит оберткой экземплярного метода, это поле содержит ссылку на объект, с которым должны выполняться операции при вызове метода обратного вызова. Т.е. это поле содержит значение, которое нужно передать в явном параметре this экземплярного метода.
|
_methodPtr
|
System.Int32
|
Внутреннее целочисленное значение, используемое CLR для идентификации метода обратного вызова.
|
_prev |
System.Multicast- Delegate |
Обычно содержит null. Оно может ссылаться на массив делегатов при создании цепочки делегатов. |
У всех делегатов конструктор принимает два параметра — ссылку на объект и целое число, ссылающееся на метод обратного вызова. Но, изучив исходный текст, можно видеть, что я передаю такие значения, как Program.FeedbackToConsole или p.FeedbackToFile. Интуиция должна подсказать вам, что этот код не должен компилироваться!
Но компилятор знает, что создается делегат, и путем синтаксического анализа кода определяет объект и метод, на которые передается ссылка. Ссылка на объект передается в параметре object конструктора, а специальное значение IntPtr (получаемое из маркеров метаданных MethodDef или MethodRef), идентифицирующее метод, передается в параметре method. В случае статического метода в параметре object передается null. Внутри конструктора значения этих параметров сохраняются в закрытых полях _target и _methodPtr. Конструктор также заносит null в поле _invocationList.
Любой объект делегата на деле является оболочкой метода и объекта, обрабатываемого этим методом. Поэтому при наличии двух следующих строк кода:
Feedback fbStatic = new Feedback(Program.FeedbackToConsole);
Feedback fblnstance = new Feedback(new Program().FeedbackToFile);
переменные fbStatic и fblnstance ссылаются на два разных инициализированных объекта-делегата Feedback (рис. 15-2).
Рис. 15-2. Переменная, ссылающаяся на делегат статического метода, и переменная, ссылающаяся на делегат экземплярного метода
В классе Delegate определены два неизменяемых открытых экземплярных свойства: Target и Method. При наличии ссылки на объект делегата можно запросить значения этих свойств.
Target возвращает ссылку на объект, обрабатываемый при обратном вызове метода. В сущности, Target возвращает значение, хранимое в закрытом поле _target. Если этот метод — статический, Target возвращает null.
Свойство Method возвращает объект SystemReflectionMethodlnfo, описывающий метод обратного вызова. В сущности, у свойства Method есть внутренний механизм, который преобразует значение из закрытого поля _methodPtr в объект Methodlnfo и возвращает его.
Эту информацию можно использовать по-разному, например, чтобы проверить, не ссылается ли объект делегата на экземплярный метод определенного типа:
Boolean DelegateRefersToInstanceMethodOfType(MulticastDelegate d, Type type)
{
return ((d.Target != null) && d.Target.GetType() == type);
}
Можно также написать код, проверяющий имя метода обратного вызова (например, FeedbackToMsgBox):
Boolean DelegateRefersToMethodOfName(MulticastDelegate d, String methodName)
{
return (d.Method.Name == methodName);
}
Есть еще масса других ситуаций, в которых могут быть полезными эти свойства.
Поговорим о том, как вызываются методы обратного вызова. Для удобства я еще раз покажу код метода Counter.
private static void Counter(Int32 from, Int32 to, Feedback fb)
{
for (Int32 val = from; val <= to; val++)
{
// Если указаны какие-либо методы обратного вызова, вызвать их.
if (fb != null)
fb(val);
}
}
Посмотрим повнимательнее на строку кода, следующую сразу после комментария. Оператор if сначала проверяет, не равна ли null переменная fb. Если нет, выполняется следующая строка, вызывающая метод обратного вызова. Проверка на неравенство null нужна, потому что fb — всего лишь переменная, которая может ссылаться на объект-делегат Feedback, но может быть и null.
Может показаться, что я вызываю функцию fb, передавая ей один параметр (val), но такой функции нет. И в этом случае компилятор генерирует код для вызова метода Invoke объекта-делегата, поскольку знает, что fb — это переменная, ссылающаяся на объект-делегат. Иначе говоря, обнаружив строку:
fb (val);
компилятор генерирует код, как если бы он увидел в исходном тексте следующее: fb.Invoke(val);
Вообще, можно в Counter вызывать Invoke явно, как здесь:
private static void Counter(Int32 from, Int32 to, Feedback fb)
{
for (Int32 val = from; val <= to; val++)
{
// Если указаны какие-либо методы обратного вызова, вызвать их.
if (fb != null)
fb.Invoke(val);
}
}
Наверное, вы помните, что компилятор определил метод Invoke при определении класса Feedback. При вызове метода Invoke он использует поля _target и _methodPtr для вызова желаемого метода на заданном объекте.
Заметьте: сигнатура метода Invoke соответствует такой же сигнатуре делегата, то есть и делегат Feedback, и (созданный компилятором) метод Invoke принимают параметр типа Int32 и возвращают void.