- •Сборки (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. Создание и использование итераторов.
- •Общие сведения о итераторах
Сравнение переменной обобщенного типа с null
Сравнение переменной обобщенного типа с null с помощью операторов «==» и «!=» допустимо, не зависимо от того, ограничен обобщенный тип или нет.
private static void ComparingAGenericTypeVariableWithNull<T>(T obj)
{
if (obj == null)
{
/* Этот код никогда не исполнится, если тип - значимый */
}
}
Так как T не ограничен, он может быть ссылочного или значимого типа. Во втором случае obj нельзя приравнять null. Обычно в таком случае компилятор C# должен выдать ошибку. Но этого не происходит — код успешно компилируется. При вызове этого метода с использованием аргумента-типа значимого типа JIT-компилятор, обнаружив, что выражение г/никогда не равно true, просто не создаст машинный код для оператора if и кода в фигурных скобках. Если бы я использовал оператор «!=», JIT-компилятор также не сгенерировал бы код для оператора if (поскольку его значение всегда true), но сгенерировал бы код из фигурных скобок после if.
Кстати, если к Tприменить ограничение struct, компиляторC# не вернет ошибку, потому что не нужно создавать код, сравнивающий значимый тип с null, — результат всегда один.
Сравнение двух переменных обобщенного типа
Сравнение двух переменных одинакового обобщенного типа допустимо только в том случае, если обобщенный параметр-тип имеет ссылочный тип.
private static void ComparingTwoGenericTypeVariables<T>(T o1, T o2)
{
if (o1 == o2) { } // Ошибка.
}
В этом примере у Tнет ограничений, и, хотя можно сравнивать две переменные ссылочного типа, сравнивать две переменные значимого типа допустимо лишь в том случае, когда значимый тип перегружает оператор ==.
Если у Tесть ограничение class, этот код скомпилируется, а оператор = вернет значение true, если переменные ссылаются на один объект и полностью тождественны. Заметьте: если Т ограничен ссылочным типом, перегружающим метод operator ==, компилятор сгенерирует вызовы этого метода при виде оператора ==. Ясно, что все вышесказанное относится и к оператору !=.
При написании кода для сравнения элементарных значимых типов (Byte, Int32 и так далее) компилятор C# сгенерирует код правильно, а для других значимых типов генерировать код для сравнений он не умеет. Поэтому, если у T метода ComparingTwoGenericTypeVariables есть ограничение struct, компилятор выдаст ошибку. А ограничивать параметр-тип значимым типом нельзя, потому что они неявно являются изолированными. Теоретически этот метод можно скомпилировать, задав в качестве ограничения конкретный значимый тип, но в таком случае метод уже не будет обобщенным. Он будет привязан к конкретному типу данных, и, конечно, компилятор не скомпилирует обобщенный метод, ограниченный одним типом.
Использование переменных обобщенного типа в качестве операндов
И, наконец, замечу, что немало трудностей несет в себе использование операторов с операндами обобщенного типа. C# умеет интерпретировать операторы (например +, -, * и /), применяемые к элементарным типам. Но эти операторы нельзя использовать с переменными обобщенного типа, потому что во время компиляции компилятор не знает их тип. Получается, что вы не сможете написать математический алгоритм для произвольных числовых типов данных. Я написал следующий обобщенный метод.
private static T Sum<T>(T num) where T : struct
{
T sum = default(T);
for (T n = default(T); n < num; n++)
sum += n; return sum;
}
Я также сделал все возможное, чтобы он скомпилировался: определил ограничение struct для T и использовал default(T), чтобы sumиnинициализировались нулем. Но при компиляции кода появились три сообщения об ошибке:
ошибка CS0019: оператор «<» нельзя применять к операндам типаTиT
ошибка CS0023: оператор «++» нельзя применять к операнду типаT
ошибка CS0019: оператор «+=» нельзя применять к операндам типаTиT
Это существенно ограничивает поддержку обобщений в CLR-среде, и многие разработчики испытали глубокое разочарование. Многие пытались создать методы, призванные обойти это ограничение с помощью отражения (главу глава 22), перегрузку оператора и т. п. Но все эти решения сильно снижают производительность или ухудшают читабельность кода.
=====================
Общие (или параметризованные) типы (generics) позволяют при описании классов, структур, методов и интерфейсов использовать параметризованные параметры (не указывать тип параметра в момент написания кода). Тип параметра определяется в момент объявления переменной соответствующего типа. Таким образом можно создать некоторый общий элемент, тип который можно использовать в дальнейшем для данных различных типов. Программисты на C++ могут углядеть с общих типах сходство с шаблонами (templates), в чем-то эта аналогия будет верна, но тут существуют некоторые ограничения.
Создадим класс, используя общие типы:
class Generics<TYPE1, TYPE2>
{
private TYPE1 mVar1;
private TYPE2 mVar2;
public Generics(TYPE1 Var1, TYPE2 Var2)
{
this.mVar1 = Var1;
this.mVar2 = Var2;
}
public string ToStringFunction(string Delemiter)
{
return this.mVar1.ToString() + Delemiter + this.mVar2.ToString();
}
public TYPE1 Variable1
{
get
{
return this.mVar1;
}
set
{
this.mVar1 = value;
}
}
public TYPE2 Variable2
{
get
{
return this.mVar2;
}
set
{
this.mVar2 = value;
}
}
}
Как видно из примера, для того чтобы использовать общие типы нужно после объявления класса указать параметризованные типы: Generics<TYPE1, TYPE2> объявляет класс с двумя параметризованными типами. Теперь используем написанный класс:
// объявление
Generics<string, string> strGeneric = new Generics<string, string>("Hello", "world");
Generics<int, int> intGeneric = new Generics<int, int>(1, 2);
Generics<string, int> strintGeneric = new Generics<string, int>("Three", 3);
int intSum;
string strSum;
// использование
intSum = intGeneric.Variable1 + intGeneric.Variable2;
strSum = strintGeneric.Variable1 + " " + strintGeneric.Variable2.ToString();
MessageBox.Show("\nstrGeneric:\n" + strGeneric.Variable1 + " " + strGeneric.Variable2 +
"\n\nintGeneric sum:\n" + intSum.ToString() +
"\n\nstrintGeneric sum:\n" + strSum.ToString());
Таким образом, очевидно, что создан класс который можно использовать с параметрами различных типов, которые устанавливаются в момент объявления класса. Аналогичным образом можно объявить структуру или интерфейс:
public struct GenericStruct<TYPE>
{
public TYPE someField;
}
public interface IGeneric<TYPE>
{
TYPE SomeMethod();
TYPE AnotherMethod();
}
Более того, параметризованные типы могут быть использованы при объявлении делегатов функций. Продемонстрирую эту возможность используя объявленный выше класс Generics.
// Объявляем делегат
public delegate DELEGATETYPE GenericDelegate<DELEGATETYPE, PARAMTYPE> (PARAMTYPE Param);
// используем делегат
Generics<string, string> strGeneric = new Generics<string, string>("Hello", "world");
GenericDelegate<string, string> genDelegate =
new GenericDelegate<string, string>(strGeneric.ToStringFunction);
// вызов делагата
MessageBox.Show(genDelegate(" my "));