- •Сборки (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. Создание и использование итераторов.
- •Общие сведения о итераторах
Обобщенные типы и наследование
Обобщенный тип, как и всякий другой, может быть производным от других типов. При использовании обобщенного типа с указанием аргументов-типов в CLR определяется новый объект-тип, производный от того же типа, что и обобщенный тип. Например, List<T> является производным от Object, поэтому List<String> и List<Guid> тоже производные от Object. Аналогично, DictionaryStringKey<TValue> — производный от Dictionary<String, TValue>, поэтому DictionaryStringKey<Guid> также производный от Dictionary<String, Guid>. Понимание того, что определение аргументов-типов не имеет ничего общего с иерархиями наследования, позволяет разобраться, какие приведения типов допустимы, а какие нет.
Например, пусть класс Node связного списка определяется следующим образом.
internal sealed class Node<T>
{
public T m_data;
public Node<T> m_next;
public Node(T data) : this(data, null) { }
public Node(T data, Node<T> next)
{
m_data = data;
m_next = next;
}
public override String ToString()
{
return m_data.ToString() + ((m_next != null) ? m_next.ToStringO : null);
}
}
Тогда код для создания связного списка будет примерно таким:
private static void SameDataLinkedList()
{
Node<Char> head = new Node<Char>('C');
head = new Node<Char>('B', head);
head = new Node<Char>('A', head);
Console.WriteLine(head.ToString());
}
В приведенном выше классе Node поле m_next должно ссылаться на другой узел, поле m_data которого содержит тот же тип данных. Это значит, что узлы связного списка должны иметь одинаковый (или производный) тип данных. Например, нельзя использовать класс Node для создания связного списка, в котором тип данных одного элемента — Char, другого — DateTime, а третьего — String.
Однако, определив необобщенный базовый класс Node, а затем — обобщенный класс TypedNode (используя класс Node как базовый), можно создать связный список с произвольным типом данных у каждого узла. Приведу определения новых классов.
internal class Node
{
protected Node m_next;
public Node(Node next)
{
m_next = next;
}
}
internal sealed class TypedNode<T> : Node
{
public T m_data;
public TypedNode(T data) : this(data, null) {}
public TypedNode(T data, Node next) : base(next)
{
m_data = data;
}
public override String ToString()
{
return m_data.ToString() + ((m_next != null) ? m_next.ToString() : null);
}
}
Теперь можно написать код для создания связного списка с разными типами данных у разных узлов. Код будет примерно таким.
private static void DifferentDataLinkedList()
{
Node head = new TypedNode<Char>('.');
head = new TypedNode<DateTime>(DateTime.Now, head);
head = new TypedNode<String>("Today is ", head);
Console.WriteLine(head.ToString());
}
Проблемы с идентификацией и тождеством обобщенных типов
Синтаксис обобщенных типов часто приводит разработчиков в замешательство. Для упрощения синтаксиса некоторые разработчики определяют новый необобщенный тип класса, производный от обобщенного типа и определяющий все необходимые аргументы-типы. Например, если нужно упростить следующий код:
List<DateTime> dt = new List<DateTime>();
некоторые разработчики сначала определят класс, вот так:
internal sealed class DateTimeList : List<DateTime>
{
// Здесь никакой код добавлять не нужно!
}
Теперь код, создающий список, можно написать проще:
DateTimeList dt = new DateTimeList();
Этот вариант удобен при использовании нового типа для параметров, локальных переменных и полей. И все же ни в коем случае нельзя явно определять новый класс лишь затем, чтобы сделать исходный текст читабельным. Причина проста: пропадает тождественность и эквивалентность типов, как видно из следующего кода:
Boolean sameType = (typeof(List<DateTime>) == typeof(DateTimeList));
При выполнении этого кода sameType инициализируется значением false, потому что сравниваются два объекта разных типов. Это также значит, что методу, в прототипе которого определено, что он принимает значение типа DateTimeList, нельзя передать List<DateTime>. Но методу, который должен принимать List<DateTime>, можно передать DateTimeList, потому что DateTimeList является производным от List<DateTime>. Запутаться в этом очень просто.
C# позволяет использовать упрощенный синтаксис для ссылки на обобщенный закрытый тип, не влияя на эквивалентность типов. Для этого в начало файла с исходным текстом нужно добавить старую добрую директиву using, вот так:
using DateTimeList = System.Collections.Generic.List<System.DateTime>;
Здесь директива using просто определяет символ DateTimeList. При компиляции кода компилятор заменяет все DateTimeList на System.Collections.Genericlist <SystemDateTime>. Таким образом, разработчики могут использовать упрощенный синтаксис, не влияя на смысл кода и тем самым сохраняя идентификацию и тождество типов. И теперь при выполнении следующей строки кода sameType инициализируется true.
Boolean sameType = (typeof(List<DateTime>) == typeof(DateTimeList));