Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ответы ОСиСП.doc
Скачиваний:
68
Добавлен:
11.05.2015
Размер:
1.78 Mб
Скачать

Инфраструктура обобщений

Для обеспечения работы обобщений Microsoft нужно было сделать следующее.

  1. Создать новые IL-команды, работающие с аргументами-типами.

  2. Изменить формат существующих таблиц метаданных для выражения имен типов и методов с обобщенными параметрами.

  3. Обновить многие языки программирования (в том числе С#, Microsoft Visual Basic .NET и другие), чтобы обеспечить поддержку нового синтаксиса и позволить разработчикам определять и ссылаться на новые обобщенные типы и методы.

  4. Изменить компиляторы для генерации новых IL-команд и измененного формата метаданных.

  5. Изменить JIT-компилятор, чтобы он обрабатывал новые IL-команды, работающие с аргументами-типами, и создавал корректный машинный код.

  6. Создать новых членов отражения, для того чтобы разработчики с помощью запроса могли проверять наличие обобщенных параметров у типов и членов. Также пришлось определить новые предоставляющие информацию отражения члены, что позволило разработчикам создавать обобщенные определения типов и методов во время исполнения.

  7. Изменить отладчик, чтобы он отображал и работал с обобщенными типами, членами, полями и локальными переменными.

  8. Изменить механизм IntelliSense в Microsoft Visual Studio для отображения конкретных прототипов членов при использовании обобщенного типа или метода с указанием типа данных.

А теперь обсудим, как работают с обобщениями внутренние механизмы CLR. Эта информация пригодится вам при проектировании и создании обобщенных алгоритмов или выборе готовых обобщенных алгоритмов.

Открытые и закрытые типы

Типы с обобщенными параметрами-типами также считаются типами, причем для каждого из них CLR создает внутренний объект-тип. Это справедливо для ссылочных типов (классов), значимых типов (структур), интерфейсов и делегатов. Но тип с обобщенными параметрами-типами называют открытым типом, а в CLR запрещено конструирование экземпляров открытых типов (как и экземпляров типов-интерфейсов).

При ссылке на обобщенный тип в коде можно определить набор обобщенных аргументов-типов. Если всем аргументам-типам определенного типа передать действительные типы данных, то такой тип будут называть закрытым. CLR разрешает создание экземпляров закрытых типов. Но в коде, ссылающемся на обобщенный тип, можно не определять все обобщенные аргументы-типы. Таким образом, в CLR создается новый объект открытого типа, экземпляры которого создавать нельзя. Следующий код проясняет ситуацию.

using System;

using System.Collections.Generic;

// Частично определенный открытый тип.

internal sealed class DictionaryStringKey<TValue> : Dictionary<String, TValue>

{}

public static class Program

{

public static void Main()

{

Object o = null;

// Dictionary<,> - это открытый тип с двумя параметрами-типами.

Type t = typeof(Dictionary<,>);

// Попытка создания экземпляра этого типа (неудачная),

o = CreateInstance(t);

Console.WriteLine();

// DictionaryStringKey<> - это открытый тип с одним параметром-типом,

t = typeof(DictionaryStringKey<>);

// Попытка создания экземпляра этого типа (неудачная),

o = CreateInstance(t);

Console.WriteLine();

// DictionaryStringKey<Guid> - это закрытый тип.

t = typeof(DictionaryStringKey<Guid>);

// Попытка создания экземпляра этого типа (удачная),

o = CreateInstance(t);

// Проверка успешности попытки.

Console.WriteLine("Object type=" + o.GetType());

Console.ReadKey();

}

private static Object CreateInstance(Type t)

{

Object o = null;

try

{

o = Activator.CreateInstance(t);

Console.Write("Created instance of {0}", t.ToString());

}

catch (ArgumentException e)

{

Console.WriteLine(e.Message);

}

return o;

}

}

Скомпилировав и выполнив этот код, вы увидите:

Cannot create an instance of System.Collections.Generic.

Dictionary'2[TKey,TValue] because Type.ContainsGenericParameters is true.

Cannot create an instance of DictionaryStringKey'1[TValue] because Type.ContainsGenericParameters is true.

Created instance of DictionaryStringKey'1[System.Guid] Object type=DictionaryStringKey'1[System.Guid]

Итак, при попытке создания экземпляра открытого типа метод Createlnstance объекта Activator генерирует исключение ArgumentException. На самом деле, сообщение об исключении означает, что тип все же содержит несколько обобщенных параметров.

В выводимой программой информации видно, что имена типов заканчиваются левой одиночной кавычкой ('), за которой следует число, означающее арность типа, то есть число необходимых для него параметров-типов. Например, арность класса Dictionary равна 2, потому что требуется определить типы ТКеу и TValue. Арность класса DictionaryStringKey — 1, так как требуется указать лишь один тип — TValue.

Также замечу, что CLR размещает статические поля типа в самом объекте-типе - поэтому у каждого закрытого типа есть свои статические поля. Иначе говоря, статические поля, определенные в объекте List<T>, не будут совместно использоваться объектами List<DateTime> и List<String>, потому что у каждого объекта закрытого типа есть свои статические поля. Если же в обобщенном типе определен статический конструктор, то последний выполняется для закрытого типа лишь раз. Иногда разработчики определяют статический конструктор для обобщенного типа, чтобы аргументы-типы соответствовали определенным критериям. Например, так определяется обобщенный тип, используемый только с перечислимыми типами.

internal sealed class GenericTypeThatRequiresAnEnum<T>

{

static GenericTypeThatRequiresAnEnum()

{

if (!typeof(T).IsEnum)

{

throw new ArgumentException("T must be an enumerated type");

}

}

}

В CLR есть функция под названием ограничения — это более удачный способ определения обобщенного типа с указанием допустимых для него аргументов-типов. Но подробнее о них чуть позже. К сожалению, эта функция не позволяет ограничить аргументы-типы только перечислимыми типами. Поэтому в предыдущем примере необходим статический конструктор для проверки того, что используемый тип является перечислимым.