
- •Сборки (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. Создание и использование итераторов.
- •Общие сведения о итераторах
Упрощенный синтаксис № 3: не нужно определять параметры метода обратного вызова
Обычная ситуация, в которой используется описанный выше упрощенный синтаксис, — когда нужно выполнить определенный код при нажатии кнопки:
buttonl.Click += delegate(Object sender, EventArgs e)
{
MessageBox.Show("The Button was clicked!");
};
Удобно иметь возможность встроить код, вызываемый по механизму обратного вызова, не определяя другой метод. Но в этом примере код обратного вызова совсем не ссылается на параметры метода обратного вызова — sender и е. Если коду обратного вызова не нужны эти параметры, С# позволяет сократить приведенный выше код до следующего:
buttonl.Click += delegate { MessageBox.Show("The Button was clicked!"); };
Заметьте: я просто удалил подстроку (Object sender, EventArgs e). Генерируя анонимный метод, компилятор также создает метод, прототип которого в точности соответствует делегату — CLR жестко требует выполнения этого условия для обеспечения безопасности типов. В этом случае компилятор все равно создает анонимный метод, соответствующий делегату EventHandler (тип делегата, ожидаемый событием Click типа Button). Однако в этом примере код анонимного метода не ссылается на параметры делегата.
Если код обратного вызова ссылается на какой-либо из параметров, после ключевого слова delegate нужно ввести круглые скобки, типы параметров и имена переменных. Возвращаемый тип все равно определяется типом делегата, и, если возвращаемый тип не void, обязательно предусмотреть оператор return во встроенном коде обратного вызова.
Упрощенный синтаксис № 4: не нужно вручную создавать обертку локальных переменных класса для передачи их в метод обратного вызова
Я уже показал, как код обратного вызова может ссылаться на другие члены класса. Однако иногда нужно, чтобы код обратного вызова ссылался на локальные параметры или переменные, имеющиеся в определяемом методе. Вот интересный пример:
internal sealed class AClass
{
public static void Using_localVariablesInTheCallbackCode(Int32 numToDo)
{
// Некоторые локальные переменные.
Int32[] squares = new Int32[numToDo];
AutoResetEvent done = new AutoResetEvent(false);
// Выполняем некоторые задачи в других потоках.
for (Int32 n = 0; n < squares.Length; n++)
{
ThreadPool.QueueUserWorkItem(
delegate(Object obj)
{
Int32 num = (Int32)obj;
// В других условиях на выполнение этой задачи требуется больше времени.
squares[num] = num * num; //<<<<< squares – внешняя переменная
// Если это последняя задача, оставляем (не закрываем) поток.
if (Interlocked.Decrement(ref numToDo) == 0)
done.Set();
},
n
);
}
// Ожидаем завершения всех остальных потоков.
done.WaitOne();
// Отображаем результаты.
for (Int32 n = 0; n < squares.Length; n++)
Console.WriteLine("Index {0}, Square={1}", n, squares[n]);
}
}
В показанном выше методе определяется один параметр, numToDo, и две локальные переменные, squares и done. А код обратного вызова в делегате ссылается на эти переменные.
А теперь представьте, что код обратного вызова размещен в отдельном методе (как того требует CLR). Как же передать значения переменных в метод обратного вызова? Единственный способ — определить новый вспомогательный класс, где также определено по полю для каждого значения, которое передается коду обратного вызова. Кроме того, код обратного вызова в этом вспомогательном классе нужно определить как экземплярный метод. В этом случае метод UsingLocalVariablesInTheCallbackCode должен создать экземпляр вспомогательного класса, инициализировать поля значениями его локальных переменных и затем создать объект-делегат, связанный с вспомогательным классом и экземплярным методом.
Это очень нудная и чреватая ошибкам работа, которую, конечно же, компилятор C# выполняет за вас автоматически. Показанный выше код компиляторC# переписывает примерно так:
internal sealed class AClass
{
public static void UsingLocalVariablesInTheCallbackCode(Int32 numToDo)
{
// Некоторые локальные переменные.
WaitCallback callback1 = null;
// Создаем экземпляр вспомогательного класса.
<>с__DisplayClass2 class1 = new <>c DisplayClass2();
// Инициализируем поля вспомогательного класса.
classl.numToDo = numToDo;
classl.squares = new Int32[class1.numToDo];
classl.done = new AutoResetEvent(false);
// Выполняем некоторые задачи в других потоках,
for (Int32 n = 0; n < classl.squares.Length; n++)
{
if (callback1 == null)
{
// Новый объект-делегат привязывается к объекту вспомогательного класса
// и к его анонимному экземплярному методу,
callback1 = new WaitCallback(classl.<UsingLocalVariablesInTheCallbackCode>b__0);
}
ThreadPool.QueueUserWorkItem(callback1, n);
}
// Ожидаем завершения всех остальных потоков.
classl.done.Wait0ne();
// Отображаем результаты.
for (Int32 n = 0; n < classl.squares.Length; n++)
Console.WriteLine("Index {0}, Square={1}", n, classl.squares[n]);
}
// Вспомогательному классу присваивается необычное имя,
// чтобы избежать возможных конфликтов и
// предотвратить доступ из внешнего класса Aclass.
[CompilerGenerated]
private sealed class <>c__DisplayClass2 : Object
{
// В коде обратного вызова каждой локальной переменной
// соответствует одно открытое поле,
public Int32[] squares;
public Int32 numToDo;
public AutoResetEvent done;
// Открытый конструктор без параметров.
public <>с DisplayClass2 { }
// Открытый экземплярный метод, содержащий код обратного вызова.
public void <UsingLocalVariablesInTheCallbackCode>b__0(0bject obj)
{
Int32 num = (Int32) obj;
squares[num] = num * num;
if (Interlocked.Decrement^ref numToDo) ==0)
done.Set();
}
}
}
Ниже приводится код, в котором использование анонимных методов смотрится исключительно гармоничным и естественным. Без анонимных методов этот код было значительно сложнее писать, читать и поддерживать.
// Создание и инициализация массива String.
String[] names = { "Jeff", "Kristin", "Aidan" };
// Извлекаем имена, в которых есть строчная буква 'i'.
Char charToFind = 'i';
names = Array.FindAll(names, delegate(String name) { return (name.IndexOf(charToFind) >= 0); });
// Преобразуем все символы строки в верхний регистр.
names = Array.ConvertAll<String, String>(names, delegate(String name) { return name.ToUpper(); });
// Сортируем имена.
Array.Sort(names, String.Compare);
// Отображаем результаты.
Array.ForEach(names, Console.WriteLine);