
- •Сборки (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. Создание и использование итераторов.
- •Общие сведения о итераторах
27. Асинхронный вызов делегатов.
28. Средства обобщенного (generic) программирования в языках C++ и C#.
Общие типы (Generics)
Обобщения (generics) —механизм, поддерживаемый общеязыковой исполняющей средой (CLR) и языками программирования, который является новой формой повторного использования кода, а именно повторным использованием алгоритма.
Разработчик определяет алгоритм, например сортировку или преобразование, но не указывает типы данных, с которыми тот работает. Поэтому алгоритм может обобщенно применяться к объектам разных типов. Используя готовый алгоритм, другой разработчик должен указать конкретные типы данных, например для алгоритма сортировки — Int32, а для алгоритма сравнения — DateTime.
CLR поддерживает создание как обобщенных ссылочных, так и обобщенных значимых типов, однако обобщенные перечислимые типы не поддерживаются. CLR позволяет создавать обобщенные интерфейсы и обобщенные делегаты. Иногда полезный алгоритм инкапсулирован в одном методе, поэтому CLR поддерживает создание обобщенных методов, определенных в ссылочном, значимом типе или в интерфейсе.
[Serializable]
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable
{
public List();
public void Add(T item);
public Int32 BinarySearch(T item);
public void Clear();
public Boolean Contains(T item);
public Int32 IndexOf(T item);
public Boolean Remove(T item);
public void Sort();
public void Sort(IComparer<T> comparer);
public void Sort(Comparison<T> comparison); public T[] ToArray();
public Int32 Count { get; }
public T this[Int32 index] { get; set; }
}
Выражением <T> сразу после имени класса автор класса List указал, что класс работает с неопределенным типом данных. При определении обобщенного типа или метода переменные, указывающие на типы (например, Т), называются параметрами-типами (type parameters). T — это имя переменной, которое применяется в исходном тексте во всех местах, где используется соответствующий тип данных.
После определения обобщенного типа List<T> готовый обобщенный алгоритм могут использовать другие разработчики, указав точный тип данных, с которым должен работать этот алгоритм. В случае обобщенного типа или метода указанный тип данных называют аргументом-типом.
Главные преимущества обобщений для разработчиков:
Защита исходного кода.Разработчику, использующему обобщенный алгоритм, не нужен доступ к исходному тексту алгоритма. А при работе с шаблонами С++ или обобщениями Java разработчику нужен был исходный текст алгоритма.
Безопасность типов.Когда обобщенный алгоритм применяется с конкретным типом, компилятор и CLR понимают это и обеспечивают, чтобы в алгоритме использовались лишь объекты, совместимые с этим типом данных. Попытка использования объекта, не совместимого с указанным типом, приведет к ошибке компиляции или ошибке во время выполнения. В этом примере передача объекта String методу Add вызвала ошибку компиляции.
Более простой и понятный код.Поскольку компилятор обеспечивает безопасность типов, в исходном тексте нужно меньше приведений типов, и такой код проще писать и поддерживать. В последней строке SomeMethod разработчику не нужно использовать приведение (DateTime), чтобы присвоить переменной dt результат индексатора (при запросе элемента с индексом 0).
Повышение производительности.До появления обобщений одним из способов определения обобщенного алгоритма было определение всех его членов так, чтобы они по определению «умели» работать с типом данных Object. Чтобы этот алгоритм работал с экземплярами значимого типа, перед вызовом членов алгоритма CLR должна была упаковать этот экземпляр. В главе 5 показано, что упаковка требует выделения памяти в управляемой куче, что приводит к более частому сбору мусора, а это, в свою очередь, ухудшает производительность приложения. Поскольку отныне обобщенный алгоритм можно создавать для работы с конкретным значимым типом, экземпляры значимого типа могут передаваться по значению и CLR не нужно выполнять упаковку. Приведения типов также не нужны (см. предыдущий пункт), поэтому CLR не нужно проверять безопасность типов при их преобразовании, что также ускоряет работу кода.
Чтобы убедить вас в том, что обобщения повышают производительность, я написал программу для сравнения производительности необобщенного алгоритма ArrayList из библиотеки классов FCL и обобщенного алгоритма List.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
public static class Program
{
public static void Main()
{
ValueTypePerfTest();
ReferenceTypePerfTest();
}
private static void ValueTypePerfTest()
{
const Int32 count = 10000000;
using (new OperationTimer("List<Int32>"))
{
List<Int32> l = new List<Int32>(count);
for (Int32 n = 0; n < count; n++)
{
l.Add(n);
Int32 x = l[n];
}
l = null; // Это должно удаляться в процессе сбора мусора.
}
using (new OperationTimer("ArrayList of Int32"))
{
ArrayList a = new ArrayList();
for (Int32 n = 0; n < count; n++)
{
a.Add(n);
Int32 x = (Int32)a[n];
}
a = null; // Это должно удаляться в процессе сбора мусора.
}
}
private static void ReferenceTypePerfTest()
{
const Int32 count = 10000000;
using (new OperationTimer("List<String>"))
{
List<String> l = new List<String>();
for (Int32 n = 0; n < count; n++)
{
l.Add("X");
String x = l[n];
}
l = null; // Это должно удаляться в процессе сбора мусора.
}
using (new OperationTimer("ArrayList of String"))
{
ArrayList a = new ArrayList();
for (Int32 n = 0; n < count; n++)
{
a.Add("X");
String x = (String)a[n];
}
a = null; // Это должно удаляться в процессе сбора мусора.
}
}
}
// Это полезный способ оценки времени выполнения алгоритма.
internal sealed class OperationTimer : IDisposable
{
private Int64 m_startTime;
private String m_text;
private Int32 m_collectionCount;
public OperationTimer(String text)
{
PrepareForOperation();
m_text = text;
m_collectionCount = GC.CollectionCount(0);
// Это выражение должно быть последним в этом методе,
// чтобы обеспечить максимально точную оценку быстродействия.
m_startTime = Stopwatch.GetTimestamp();
}
public void Dispose()
{
Console.WriteLine("{0,6:###.00} seconds (GCs={1,3}) {2}",
(Stopwatch.GetTimestamp() - m_startTime) /
(Double)Stopwatch.Frequency, GC.CollectionCount(0) - m_collectionCount, m_text);
}
private static void PrepareForOperation()
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
}
Скомпилировав эту программу в режиме Release (с включенной оптимизацией) и выполнив ее на своем компьютере, я получил такой результат:
.10 seconds (GCs= 0) List<Int32> 2.02 seconds (GCs= 30) ArrayList of Int32 .52 seconds (GCs= 6) List<String> .53 seconds (GCs= 6) ArrayList of String
Как видите, обобщенный алгоритм List с типом lnt32 работает гораздо быстрее, чем необобщенный алгоритм ArrayList с тем же типом. Кроме того, использование значимого типа (Int32) с ArrayList требует много операций упаковки, и, как результат, 30 сборок мусора, а в алгоритме List сбор мусора вообще не нужен.
Результаты проверки ссылочного типа не столь впечатляющие: временные показатели и число сборок мусора здесь примерно одинаковы. В данном случае у обобщенного алгоритма List реальных преимуществ нет.
Необходимо понимать, что CLR генерирует машинный код для любого метода при первом его вызове в применении к конкретному типу данных. Это увеличивает размер рабочего набора приложения и ухудшает производительность.