- •Сборки (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. Создание и использование итераторов.
- •Общие сведения о итераторах
Упрощение синтаксиса работы с делегатами в с#
Большинство программистов считает, что работать с делегатами слишком обременительно. Причина в очень сложном синтаксисе. Возьмем к примеру такую строку кода:
button1.Click += new EventHandler(button1_Click);
где button1.Click — это метод, выглядящий примерно так:
void button1_Click(Object sender, EventArgs e)
{
// Кнопка щелкнута - нужно выполнять соответствующие действия...
}
Идея первой строки кода — зарегистрировать адрес метода button1_Click в элементе управления «кнопка» с тем, чтобы по щелчку кнопки вызывался этот метод. Большинству программистов кажется совершенно неестественным создавать объект-делегат EventHandler только для того, чтобы определить адрес метода button1_Click. Однако создание объекта-делегата EventHandler необходимо CLR, так как этот объект служит оберткой, которая обеспечивает вызов метода со строгим соблюдением безопасности типов. Обертка также поддерживает вызов экземплярных методов и создание цепочек делегатов. К сожалению, большинство программистов не хочет думать о таких деталях. Программисты предпочли бы записать приведенный выше код так:
button1.Click += button1_Click;
К счастью, компилятор С# от Microsoft поддерживает несколько способов упрощения синтаксиса при работе с делегатами. Сейчас я об этом расскажу поподробнее, но, прежде чем начать, сделаю одно важное замечание. То, что вы сейчас узнаете, — всего лишь ряд упрощений синтаксиса в C#, которые облегчают задачу программистам по созданию IL-кода, который необходим для нормальной работы CLR и других языков программирования с делегатами. Это также означает, что все сказанное ниже относится исключительно кC# — другие компиляторы скорее всего не поддерживают такой упрощенный синтаксис делегатов.
Упрощенный синтаксис № 1: не нужно создавать объект-делегат
Как я уже показывал, С# позволяет определять имя метода обратного вызова, не создавая объект-делегат, служащий оберткой. Вот еще один пример:
internal sealed class AClass
{
public static void CallbackWithoutNewingADelegateObject()
{
ThreadPool.QueueUserWorkItem(SomeAsyncTask, 5);
}
private static void SomeAsyncTask(Object o)
{
Console.WriteLine(o);
}
}
Здесь статический метод QueueUserWorkltem класса ThreadPool ожидает ссылку на объект-делегат WaitCallback, который в свою очередь содержит ссылку на метод SomeAsyncTask. Так как компилятор С# сам «догадывается», что имеется в виду, я могу опустить код создания объекта-делегата WaitCallback, что делает код намного читабельнее и понятнее. Конечно, в процессе компиляции компилятор С# автоматически создает IL-код создания нового объекта-делегата WaitCallback.
Упрощенный синтаксис № 2: не нужно определять метод обратного вызова
В приведенном выше коде имя метода обратного вызова, SomeAsyncTask, передается методу QueueUserWorkltem класса ThreadPool. С# позволяет встраивать реализацию метода обратного вызова непосредственно в код, а не в сам метод. Например, приведенный выше код можно переписать так.
internal sealed class AClass
{
public static void CallbackWithoutNewingADelegateObject()
{
ThreadPool.QueueUserWorkItem(delegate(Object obj) { Console.WriteLine(obj); }, 5);
}
}
Заметьте: первый «параметр» метода QueueUserWorkltem представляет собой блок кода! Обнаружив ключевое слово delegate в месте, где ожидается ссылка на объект- делегат, компилятор С# автоматически определяет в классе новый закрытый метод (здесь это класс AClass). Этот новый метод называют анонимным из-за того, что компилятор создает имя метода автоматически и обычно вы даже никогда не узнаете его имя. Однако можно прибегнуть к ILDasm.exe, чтобы изучить сгенерированный компилятором код. После компиляции приведенного выше кода с помощью ILDasm.exe я увидел, что компилятор С# решил назвать этот метод <CallbackWithoutNewingADelegateObject>b__0. Но будьте осторожны: никогда не стоит рассчитывать на то, что компилятор создаст именно такое имя метода, потому что в следующей версии компилятора С# алгоритм генерации имени метода может измениться.
Используя ILDasm.exe, также можно заметить, что компилятор С# применяет к этому методу атрибут SystemRuntime.CompilerServices.CompilerGeneratedAttribute для указания того, что метод создан компилятором, а не программистом. Код из «параметра» размещается в сгенерированном компилятором методе.
ПримечаниеЧисло операторов или их типов, которые можно использовать в коде обратного вызова (анонимном методе), не ограничено. Однако при создании анонимного метода никак нельзя применить к методу нестандартный (пользовательский) атрибут. Более того — по отношению к методу нельзя применить никаких модификаторов метода (например, unsafe).
Сгенерированные компилятором анонимные методы практически всегда закрыты, а метод — статический или нестатический, в зависимости от того, к каким членам экземпляра обращается метод. Так что нет никакой нужды применять к методу такие модификаторы, как public, protected, internal, virtual, sealed, override и abstract.
Наконец, при компиляции приведенного выше кода компилятор C# перепишет код примерно так:
internal sealed class AClass
{
// Это закрытое поле создается для кеширования объекта-делегата.
// Преимущество: CallbackWithoutNewingADelegateObject не будет создавать
// новый объект при каждом вызове.
// Недостаток: объект в кеше никогда не убирается сборщиком мусора.
[CompilerGenеrated]
private static WaitCallback <>9__CachedAnonymousMethodDelegate1;
public static void CallbackWithoutNewingADelegateObject()
{
if (<>9__CachedAnonymousMethodDelegate1 == null)
{
// При первом вызове объект-делегат создается и кешируется.
<>9__CachedAnonymousMethodDelegate1 =
new WaitCallback(<CallbackWithoutNewingADelegateObject>b__0);
}
ThreadPool.QueueUserWorkItem(<>9__CachedAnonymousMethodDelegate1, 5);
}
[CompilerGenеrated]
private static void <CallbackWithoutNewingADelegateObject>b__0(Object obj)
{
Console.WriteLine(obj);
}
}
Прототип анонимного метода должен соответствовать типу делегата WaitCallback: возвращать void и принимать параметр Object. Однако я создавал анонимный метод, поэтому определил имя параметра, разместив (Object obj) после ключевого слова delegate.
Анонимный метод объявляется закрытым; это запрещает любому коду, за исключением кода типа, доступ к методу (хотя отражение позволит узнать, что метод существует). Заметьте также, что анонимный метод определен как статический, потому что код не обращается ни к каким членам экземпляра (да и не сможет этого сделать, так как CallbackWithoutNewingADelegate-Object — сам по себе статический метод). Однако код может сослаться на любые определенные в классе статические поля или статические методы. Вот пример:
internal sealed class AClass
{
private static String sm_name; // Статическое поле.
public static void CallbackWithoutNewingADelegateObject()
{
ThreadPool.QueueUserWorkItem(
// Код обратного вызова может ссылаться на статические члены.
delegate(Object obj) { Console.WriteLine(sm_name + ": " + obj); }, 5
);
}
}
Если бы метод CallbackWithoutNewingADelegateObject не был статическим, код анонимного метода мог бы содержать ссылки на члены экземпляра. Но даже в отсутствие в коде ссылок на члены экземпляра компилятор создаст статический анонимный метод, так как он эффективнее, чем экземплярный метод, потому что дополнительный параметр this не нужен. Но, если код анонимного метода действительно ссылается на член экземпляра, компилятор создаст нестатический анонимный метод:
internal sealed class AClass
{
private String m_name; // Зкземплярное поле.
// Экземплярный метод.
public void CallbackWithoutNewingADelegateObject()
{
ThreadPool.QueueUserWorkItem(
// Код обратного вызова может ссылаться на члены экземпляра.
delegate(Object obj) { Console.WriteLine(m_name + ": " + obj); }, 5
);
}
}