КАТЕГОРИИ:
АстрономияБиологияГеографияДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРиторикаСоциологияСпортСтроительствоТехнологияФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника
|
Открытые и закрытые типыТипы с обобщенными параметрами-типами также считаются типами, причем для каждого из них 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 есть функция под названием ограничения — это более удачный способ определения обобщенного типа с указанием допустимых для него аргументов-типов. Но подробнее о них чуть позже. К сожалению, эта функция не позволяет ограничить аргументы-типы только перечислимыми типами. Поэтому в предыдущем примере необходим статический конструктор для проверки того, что используемый тип является перечислимым.
|