Студопедия
Случайная страница | ТОМ-1 | ТОМ-2 | ТОМ-3
АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатика
ИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханика
ОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторика
СоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансы
ХимияЧерчениеЭкологияЭкономикаЭлектроника

Источник универсального параметра

Читайте также:
  1. II. Основные нормативные источники
  2. V. Рекомендуемые источники
  3. VII. Иконографические источники
  4. Аварийный источник электроэнергии на грузовых судах
  5. Аварийный источник электроэнергии на пассажирских судах
  6. Актовый материал как исторический источник и методы их изучения
  7. БЛОК 5. ЖУРНАЛИСТ И ИСТОЧНИК ИНФОМАЦИИ. Источник информации как объект нравственного отношения журналиста. Способы, методика сбора информации: нравственный аспект.

Что такое «связывание» и что делает его поздним?

 

«Позднее связывание» – это один из таких же терминов компьютерных наук, что «строгая типизация», который означает разные вещи для разных людей. Я думаю, что смогу описать, что я под ним понимаю.

 

Прежде всего, что такое «связывание»? Мы не сможем понять, что означает позднее связывание, если мы не знаем, что вообще означает термин «связывание».

 

По определению, компилятор – это такое устройство, которое принимает текст, написанный на одном языке, и выдает код на другом языке, «который означает то же самое». Я, например, разрабатываю компилятор, который принимает на вход текст на языке C# и выдает CIL (*). Все важные задачи, выполняемые компилятором можно разделить на три крупные группы:

 

Синтаксический анализ входного текста

Семантический анализ синтаксиса

Генерация выходного текста – в этой статье этот этап нам не интересен

Синтаксический анализ входного текста ничего не знает о значении анализируемого текста; синтаксический анализ беспокоится, прежде всего, о лексической структуре программы (т.е. о границах комментариев, идентификаторах, операторах и т.п.), а затем по этой лексической структуре определяется грамматическая структура программы: границы классов, методов, операторов, выражений и т.п.

 

Затем семантический анализатор берет результаты синтаксического анализатора и связывает значения различных синтаксических элементов. Например, когда вы пишите:

 

class X {}

class B {}

class D: B

{

public static void X() { }

public static void Y() { X(); }

}

 

то синтаксический анализатор определяет наличие трех классов, что один из них содержит два метода, второй метод содержит оператор, который является выражением вызова метода. Семантический анализатор определяет, что X в выражении X(); ссылается на метод D.X(), а не, скажем, на тип X, объявленный выше. Это и есть пример «связывания» в наиболее широком смысле этого слова: связывание – это ассоциация синтаксического элемента, содержащего имя метода, с логической частью программы.

 

Когда речь заходит о «раннем» или «позднем» «связывании», то речь всегда идет об определении имени для вызова метода. Однако, с моей точки зрения это определение слишком строгое. Я буду использовать термин «связывание» при описании процесса определения семантическим анализатором компилятора, что класс D наследует класс B и что имя «B» связано с именем класса.

 

Более того, я буду использовать термин «связывание» для описания и других видов анализа. Если у вас в программе есть выражение 1 * 2 + 1.0, тогда я могу сказать, что оператор «+» связан со встроенным оператором, который принимает два числа с плавающей запятой, складывает их и возвращает третье число. Обычно люди не думают о связи имени «+» с определенным методом, но я, все же, считаю это «связыванием».

 

Говоря еще менее строго, я могу использовать термин «связывание» для нахождения ассоциации типов с выражениями, которые не используют имя этого типа напрямую. Если говорить неформально, то в приведенном выше примере выражение 1 * 2 «связано» с типом int, хотя, очевидно, имя этого типа в нем не указано. Синтаксическое выражение строго связано с этим семантическим элементом, хотя и не использует соответствующее имя напрямую.

 

Так что, говоря в общем случае, я бы сказал, что «связывание» – это любая ассоциация некоторого фрагмента синтаксического дерева с некоторым логическим элементом программы. (**)

 

Тогда в чем разница между «ранним» и «поздним» связыванием? Люди часто говорят об этих понятиях так, будто это взаимоисключающий выбор: связывание либо раннее, либо позднее. Как мы вскоре увидим, это не так; некоторые виды связывания полностью ранние, некоторые частично ранние и частично – поздние, а некоторые – и правда, полностью поздние. Но прежде чем переходить к этому, давайте рассмотрим, по отношению к чему связывание бывает ранним или поздним?

 

Обычно, когда мы говорим о «раннем связывании» мы имеем ввиду «связывание, выполняемое компилятором и результат связывания «зашивается» в сгенерированный код»; если связывание завершается неудачно, то программа не запускается, поскольку компилятор не может перейти к фазе генерации кода. Под «поздним связыванием» мы подразумеваем, что «некоторая часть связывания будет выполняться во время выполнения» и, таким образом, ошибки связывания проявятся только во время выполнения. Раннее и позднее связывание иногда называют «статическим» и «динамическим связыванием»; статическое связывание выполняется на основе «статической» информации, известной компилятору, а динамическое связывание выполняется на основе «динамической» информации, известной только во время выполнения.

 

Какое из этих видов связывания лучше? Очевидно, что ни один из вариантов не является однозначно лучше другого; если бы один из вариантов всегда превосходил другой, то мы бы с вами ничего сейчас не обсуждали. Преимущество раннего связывания в том, что мы можем быть уверены в отсутствии ошибок времени выполнения; недостатком же является отсутствие гибкости позднего связывания. Раннее связывание предполагает, что вся информация, необходимая для принятия правильного решения будет известна перед выполнением программы; но иногда эта информация не доступна до момента выполнения.

 

Я уже говорил, что связывание образует спектр от раннего до позднего. Давайте рассмотрим некоторые примеры на языке C#, которые покажут, как мы можем перейти от раннего связывания к позднему.

 

Мы начали с примера вызова статического метода Х. Этот анализ однозначно является ранним. Нет никакого сомнения в том, что при вызове метода Y, будет вызван метод D.X. Никакая часть этого анализа не откладывается до времени выполнения, поэтому данный вызов будет однозначно успешным.

 

Теперь, давайте рассмотрим следующий пример:

 

class B

{

public void M(double x) {}

public void M(int x) {}

}

class C

{

public static void X(B b, int d) { b.M(d); }

}

 

Теперь у нас меньше информации. Мы выполняем много раннего связывания; мы знаем, что переменная b типа B, и что вызывается метод B.M(int). Но, в отличие от предыдущего примера, у нас нет никаких гарантий компилятора, что вызов будет успешным, поскольку переменная b может быть null. По сути, мы откладываем до времени выполнения анализ того, будет ли приемник вызова валидным или нет. Многие не рассматривает это решение, как «связывание», поскольку мы не связываем синтаксис с программным элементом. Давайте сделаем вызов внутри метода C немного более поздним, путем изменения класса B:

 

class B

{

public virtual void M(double x) {}

public virtual void M(int x) {}

}

 

Теперь мы выполняем часть анализа во время компиляции; мы знаем, что будет вызван виртуальный метод B.M(int). Мы знаем, что вызов метода будет успешен, в том плане, что такой метод существует. Но мы не знаем, какой именно метод будет вызван во время выполнения! Это может быть переопределенный метод в наследнике; может быть вызван совершенно другой код, определенный в другой части программы. Диспетчеризация виртуальных методов является формой позднего связывания; решение о том, какой метод связан с синтаксической конструкцией b.M(d) частично принимается компилятором, а частично – во время выполнения.

 

А как насчет такого примера?

 

class C

{

public static void X(B b, dynamic d) { b.M(d); }

}

 

Теперь связывание практически полностью отложено до времени выполнения. В этом случае компилятор генерирует код, который говорит динамической среде времени выполнения (Dynamic Language Runtim), что статический анализ определил, что статическим типом переменной b является класс B и что вызываемой метод называется M, но реальное разрешение перегрузки для определения метода B.M(int) или B.M(double) (или никакого из них, если d, например, будет типа string) будет выполнено во время выполнения на основе этой информации. (***)

 

class C

{

public static void X(dynamic b, dynamic d) { b.M(d); }

}

 

Теперь, на этапе компиляции определяется лишь то, что для некоторого типа вызывается метод с именем M. Это практически наиболее позднее связывание, но, на самом деле, мы можем пойти еще дальше:

 

class C

{

public static void X(object b, object[] d, string m, BindingFlags f)

{

b.GetType().GetMethod(m, f).Invoke(b, d);

}

}

 

Теперь весь анализ выполняется во время позднего связывания; мы даже не знаем, какое имя мы собираемся связывать с вызываемым методом. Все, что мы можем знать, так это то, что автор X ожидает, что в переданном объекте b есть метод, имя которого определяет m, соответствующий флагам, переданным в f, принимающий аргументы, переданные в d. В этом случае мы ничего не можем сделать во время компиляции. (****)

 

Отражение (C# и Visual Basic)

 

Механизм отражения позволяет получать объекты (типа Type), которые описывают сборки, модули и типы. Отражение можно использовать для динамического создания экземпляра типа, привязки типа к существующему объекту, а также получения типа из существующего объекта и вызова его методов или доступа к его полям и свойствам. Если в коде используются атрибуты, отражение обеспечивает доступ к ним. Дополнительные сведения см. в разделе Расширение метаданных с помощью атрибутов.

Ниже приведен простой пример отражения, в котором для получения типа переменной используется статический метод GetType, наследуемый всеми типами из базового класса Object.

 

// Using GetType to obtain type information:

int i = 42;

System.Type type = i.GetType();

System.Console.WriteLine(type)

 

Результат выполнения кода:

System.Int32

 

В следующем примере отражение используется для получения полного имени загруженной сборки.

 

// Using Reflection to get information from an Assembly:

System.Reflection.Assembly info = typeof(System.Int32).Assembly;

System.Console.WriteLine(info);

 

Результат выполнения кода:

mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

 

Примечание!

Ключевые слова C# protected и internal не имеют никакого значения в промежуточном языке и не используются в интерфейсах API отражения. Соответствующими терминами в промежуточном языке являются Family и Assembly. Для идентификации метода internal с помощью отражения используется свойство IsAssembly. Для идентификации метода protected internal используется свойство IsFamilyOrAssembly.

Общие сведения об отражении

· Отражение удобно использовать в следующих ситуациях.

· При необходимости доступа к атрибутам в метаданных программы. Дополнительные сведения см. в разделе Извлечение информации, сохраненной в атрибуте.

· Для проверки и создания экземпляров типов в сборке.

· Для создания новых типов во время выполнения. Используйте классы в System.Reflection.Emit.

· Для выполнения позднего связывания, которое обеспечивает доступ к методам в типах, созданных во время выполнения. См. раздел Динамическая загрузка и использование типов.

 

 

Отражение в.NET Framework

 

Классы в пространстве имен System.Reflection вместе с System.Type позволяют получать сведения о загруженных сборках и определенных в них типах, таких как классы, интерфейсы и типы значений. Отражение можно также использовать для создания экземпляров типов во время выполнения, для вызова этих экземпляров и получения доступа к ним. Разделы, описывающие конкретные аспекты отражения, см. в подразделе Связанные разделы в конце данного обзора.

Загрузчик среды CLR управляет доменами приложений, которые образуют определенные границы вокруг объектов с одинаковой областью приложения. В частности, он загружает каждую сборку в соответствующий домен приложения и контролирует распределение памяти для иерархии типов в каждой сборке.

Сборки содержат модули, модули содержат типы, которые, в свою очередь, содержат члены. Механизм отражения позволяет получать объекты, которые инкапсулируют сборки, модули и типы. Отражение можно использовать для динамического создания экземпляра типа, привязки типа к существующему объекту или получения типа существующего объекта. После этого можно вызывать методы типа или обращаться к его полям и свойствам. Наиболее распространены следующие примеры использования отражения.

Объект Assembly используется для определения и загрузки сборок, загрузки модулей, перечисленных в манифесте сборки, а также для поиска типа в сборке и создания его экземпляра.

Объект Module используется для получения таких сведений, как имя сборки, содержащей данный модуль, и перечень классов этого модуля. Кроме того, можно получить все глобальные методы или определенные неглобальные методы, включенные в модуль.

Класс ConstructorInfo используется для получения таких сведений, как имя, параметры, модификаторы доступа (например, public или private) и параметры реализации конструктора (например abstract или virtual). Для вызова конкретного конструктора используется метод GetConstructors или GetConstructor определенного типа Type.

Класс MethodInfo используется для получения таких сведений, как имя, возвращаемый тип, параметры, модификаторы доступа (например, public или private) и параметры реализации метода (например abstract или virtual). Для вызова конкретного метода используется метод GetMethods или GetMethod типа Type.

Класс FieldInfo позволяет получить такие сведения как имя, модификаторы доступа (например, public или private) и параметры реализации (например static) поля, а также чтобы установить или получить значения полей.

Класс EventInfo используется для получения таких сведений о событии, как имя, тип данных обработчика события, пользовательские атрибуты, объявленный тип и отраженный тип, а также для добавления и удаления обработчиков событий.

Класс PropertyInfo позволяет получить такие сведения о свойстве, как имя, тип данных, объявленный тип, отраженный тип и состояние (доступ только для чтения или для чтения и записи), а также получать и задавать значения свойств.

Класс ParameterInfo позволяет получить такие сведения, как имя параметра, тип данных, вид параметра (входной или выходной) и расположение параметра в сигнатуре метода.

Класс CustomAttributeData используется для получения сведений о пользовательских атрибутах при работе в контексте домена приложения, предназначенном только для отражения. Класс CustomAttributeData позволяет проверить атрибуты, не создавая их экземпляров.

Классы пространства имен System.Reflection.Emit реализуют особый вид отражения, который позволяет создавать типы во время выполнения.

Отражение также может использоваться для создания приложений, называемых обозревателями типов, которые позволяют пользователям выбирать типы и затем просматривать сведения о них.

Есть и другие способы использования отражения. Компиляторы таких языков программирования, как Jscript, используют отражение для построения таблиц символических имен. Классы в пространстве имен System.Runtime.Serialization при помощи отражения осуществляют доступ к данным и определяют, какие поля следует сохранить. Классы в пространстве имен System.Runtime.Remoting используют отражение косвенным путем при сериализации.

 

Типы среды выполнения в отражении

 

Отражение предоставляет классы, такие как Type и MethodInfo, для представления типов, элементов, параметров и других объектов кода. Однако при использовании отражения работа происходит не напрямую с этими классами, большинство из которых являются абстрактными (MustInherit в Visual Basic). Вместо этого работа происходит с типами, предоставленными средой CLR.

Например, при использовании оператора C# typeof (GetType в Visual Basic) для получения объекта Type, объект фактически относится к типу RuntimeType. RuntimeType является производным от Type и предоставляет реализации абстрактных методов.

Этими классами среды выполнения являются internal (Friend в Visual Basic). Они не документируются отдельно от своих базовых классов, потому что их поведение описывается в документации базового класса.

 

Просмотр сведений о типах

 

Класс System.Type является центральной частью отражения. Среда CLR создает объект Type для загруженного типа, когда он запрашивается отражением. Используя методы, поля, свойства и вложенные классы объекта Type, можно получить полные сведения об этом типе.

Используйте перегрузку Assembly.GetType или метод Assembly.GetTypes для получения объектов Type из сборок, которые не были загружены, передавая имя необходимого типа или типов. Используйте перегрузку Type.GetType для получения объектов Type из уже загруженной сборки. Используйте перегрузку Module.GetType и метод Module.GetTypes для получения объектов модуля Type.

Примечание Примечание

Если планируется выполнение проверок и управление универсальными типами и методами, рекомендуется ознакомиться с дополнительными сведениями, приведенными в разделах Отражение и универсальные типы и Практическое руководство. Изучение универсальных типов и создание их экземпляров при помощи отражения.

В следующем примере показан синтаксис для получения объекта Assembly и модуля сборки.

 

// Gets the mscorlib assembly in which the object is defined.

Assembly a = typeof(object).Module.Assembly;

 

В следующем примере показано, как получать объекты Type из загруженной сборки.

 

// Loads an assembly using its file name.

Assembly a = Assembly.LoadFrom("MyExe.exe");

// Gets the type names from the assembly.

Type[] types2 = a.GetTypes();

foreach (Type t in types2)

{

Console.WriteLine(t.FullName);

}

 

После получения объекта Type сведения об элементах этого типа можно получить разными способами. Например, можно узнать обо всех элементах этого типа посредством вызова метода Type.GetMembers, который получит массив объектов MemberInfo, где будут описаны все элементы текущего типа.

Можно также использовать методы класса Type для получения сведений об одном или нескольких конструкторах, методах, событиях, полях или свойствах, заданных по имени. Например, Type.GetConstructor инкапсулирует определенный конструктор текущего класса.

Если известен Type, можно использовать свойство Type.Module для получения объекта, который инкапсулирует модуль, содержащий этот тип. Используйте свойство Module.Assembly для обнаружения объекта, который инкапсулирует сборку, содержащую модуль. Можно напрямую получить сборку, которая инкапсулирует тип, посредством использования свойства Type.Assembly.

System.Type и ConstructorInfo

В следующем примере показано, как отображать список конструкторов класса, в этом случае, для класса String.

 

// This program lists all the public constructors

// of the System.String class.

using System;

using System.Reflection;

 

class ListMembers

{

public static void Main()

{

Type t = typeof(System.String);

Console.WriteLine("Listing all the public constructors of the {0} type", t);

// Constructors.

ConstructorInfo[] ci = t.GetConstructors(BindingFlags.Public | BindingFlags.Instance);

Console.WriteLine("//Constructors");

PrintMembers(ci);

}

 

public static void PrintMembers(MemberInfo[] ms)

{

foreach (MemberInfo m in ms)

{

Console.WriteLine("{0}{1}", " ", m);

}

Console.WriteLine();

}

}

 

 

MemberInfo, MethodInfo, FieldInfo и PropertyInfo

Получение сведений о методах, свойствах, событиях и полях типа с помощью объектов MemberInfo, MethodInfo, FieldInfo и PropertyInfo.

В следующем примере используются объектMemberInfo для вывода количества элементов в классе System.IO.File и свойство System.Type.IsPublic для определения степени доступности этого класса.

 

using System;

using System.IO;

using System.Reflection;

 

class Mymemberinfo

{

public static void Main()

{

Console.WriteLine ("\nReflection.MemberInfo");

// Gets the Type and MemberInfo.

Type MyType = Type.GetType("System.IO.File");

MemberInfo[] Mymemberinfoarray = MyType.GetMembers();

// Gets and displays the DeclaringType method.

Console.WriteLine("\nThere are {0} members in {1}.",

Mymemberinfoarray.Length, MyType.FullName);

Console.WriteLine("{0}.", MyType.FullName);

if (MyType.IsPublic)

{

Console.WriteLine("{0} is public.", MyType.FullName);

}

}

}

 

 

В следующем примере анализируется тип указанного элемента. Выполняется отражение элемента класса MemberInfo и выводятся сведения о его типе.

 

// This code displays information about the GetValue method of FieldInfo.

using System;

using System.Reflection;

 

class MyMethodInfo

{

public static int Main()

{

Console.WriteLine("Reflection.MethodInfo");

// Gets and displays the Type.

Type MyType = Type.GetType("System.Reflection.FieldInfo");

// Specifies the member for which you want type information.

MethodInfo Mymethodinfo = MyType.GetMethod("GetValue");

Console.WriteLine(MyType.FullName + "." + Mymethodinfo.Name);

// Gets and displays the MemberType property.

MemberTypes Mymembertypes = Mymethodinfo.MemberType;

if (MemberTypes.Constructor == Mymembertypes)

{

Console.WriteLine("MemberType is of type All");

}

else if (MemberTypes.Custom == Mymembertypes)

{

Console.WriteLine("MemberType is of type Custom");

}

else if (MemberTypes.Event == Mymembertypes)

{

Console.WriteLine("MemberType is of type Event");

}

else if (MemberTypes.Field == Mymembertypes)

{

Console.WriteLine("MemberType is of type Field");

}

else if (MemberTypes.Method == Mymembertypes)

{

Console.WriteLine("MemberType is of type Method");

}

else if (MemberTypes.Property == Mymembertypes)

{

Console.WriteLine("MemberType is of type Property");

}

else if (MemberTypes.TypeInfo == Mymembertypes)

{

Console.WriteLine("MemberType is of type TypeInfo");

}

return 0;

}

}

 

В следующем примере используются все классы отражения *Info вместе с классом BindingFlags для получения сведений о всех элементах (конструкторах, полях, свойствах, событиях и методах) указанного класса, причем статические элементы выводятся отдельно от элементов экземпляров.

 

// This program lists all the members of the

// System.IO.BufferedStream class.

using System;

using System.IO;

using System.Reflection;

 

class ListMembers

{

public static void Main()

{

// Specifies the class.

Type t = typeof(System.IO.BufferedStream);

Console.WriteLine("Listing all the members (public and non public) of the {0} type", t);

 

// Lists static fields first.

FieldInfo[] fi = t.GetFields(BindingFlags.Static |

BindingFlags.NonPublic | BindingFlags.Public);

Console.WriteLine("// Static Fields");

PrintMembers(fi);

 

// Static properties.

PropertyInfo[] pi = t.GetProperties(BindingFlags.Static |

BindingFlags.NonPublic | BindingFlags.Public);

Console.WriteLine("// Static Properties");

PrintMembers(pi);

 

// Static events.

EventInfo[] ei = t.GetEvents(BindingFlags.Static |

BindingFlags.NonPublic | BindingFlags.Public);

Console.WriteLine("// Static Events");

PrintMembers(ei);

 

// Static methods.

MethodInfo[] mi = t.GetMethods (BindingFlags.Static |

BindingFlags.NonPublic | BindingFlags.Public);

Console.WriteLine("// Static Methods");

PrintMembers(mi);

 

// Constructors.

ConstructorInfo[] ci = t.GetConstructors(BindingFlags.Instance |

BindingFlags.NonPublic | BindingFlags.Public);

Console.WriteLine("// Constructors");

PrintMembers(ci);

 

// Instance fields.

fi = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic |

BindingFlags.Public);

Console.WriteLine("// Instance Fields");

PrintMembers(fi);

 

// Instance properites.

pi = t.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic |

BindingFlags.Public);

Console.WriteLine ("// Instance Properties");

PrintMembers(pi);

 

// Instance events.

ei = t.GetEvents(BindingFlags.Instance | BindingFlags.NonPublic |

BindingFlags.Public);

Console.WriteLine("// Instance Events");

PrintMembers(ei);

 

// Instance methods.

mi = t.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic

| BindingFlags.Public);

Console.WriteLine("// Instance Methods");

PrintMembers(mi);

 

Console.WriteLine("\r\nPress ENTER to exit.");

Console.Read();

}

 

public static void PrintMembers (MemberInfo [] ms)

{

foreach (MemberInfo m in ms)

{

Console.WriteLine ("{0}{1}", " ", m);

}

Console.WriteLine();

}

}

 

Отражение и универсальные типы

 

.NET Framework 4.5 Другие версии Эта тема еще не получила оценку - Оценить эту тему

С точки зрения отражения разница между универсальным и обычным типом заключается в том, что универсальный тип связан с набором параметров типа (если это определение универсального типа) или с аргументами типа (если это сконструированный тип). Тем же отличается и универсальный метод от обычного.

Есть два ключевых момента, позволяющих понять, как в отражении обрабатываются универсальные типы и методы:

Параметры типов для определений универсальных типов и определений универсальных методов представлены экземплярами класса Type.

Примечание Примечание

Поведение многих типов и методов класса Type отличается, если объект Type представляет параметр универсального типа. Эти различия документируются в темах, посвященных конкретным свойствам и методам. Примеры см. в разделах IsAutoClass и DeclaringType. В дополнение к этому некоторые члены являются допустимыми только в том случае, если объект Type представляет параметр универсального типа. Например, см. GetGenericTypeDefinition.

Если экземпляр класса Type представляет универсальный тип, он содержит массив типов, которые представляют параметры типа (для определений универсальных типов) или аргументы типов (для сконструированных типов). Это также справедливо для экземпляра класса MethodInfo, который представляет универсальный метод.

Отражение содержит методы классов Type и MethodInfo, которые позволяют получить доступ к массиву параметров типа и определить, представляет ли экземпляр класса Type параметр типа или фактический тип.

Пример, в котором демонстрируются рассматриваемые здесь методы, см. в разделе Практическое руководство. Изучение универсальных типов и создание их экземпляров при помощи отражения.

В следующем ниже обсуждении предполагается определенное знакомство с терминологией универсальных шаблонов, например знание того, в чем состоит отличие между параметрами типа и аргументами типа, а также открытыми и закрытыми сконструированными типами. Для получения дополнительной информации см. Универсальные шаблоны в платформе.NET Framework.

Этот обзор содержит следующие подразделы:

· Универсальный тип или универсальный метод

· Создание закрытых универсальных типов

· Изучение аргументов типа и параметров типа

· Инварианты

· Связанные разделы

· Универсальный тип или универсальный метод

При использовании отражения для изучения неизвестного типа, представленного экземпляром класса Type, следует использовать свойство IsGenericType, которое позволяет определить, является ли неизвестный тип универсальным. Если тип является универсальным, это свойство возвращает значение true. Аналогичным образом при изучении неизвестного метода, представленного экземпляром класса MethodInfo, для определения того, является ли этот метод универсальным, следует использовать свойство IsGenericMethod.

Наличие определения универсального типа или метода

Свойство IsGenericTypeDefinition используется для определения того, представляет ли объект Type определение универсального типа, а метод IsGenericMethodDefinition — для определения того, представляет ли объект MethodInfo определение универсального метода.

Определения универсальных типов и методов являются шаблонами, на основе которых создаются типы, поддерживающие создание экземпляров. Универсальные типы в библиотеке классов.NET Framework, например Dictionary<TKey, TValue>, являются определениями универсальных типов.

 

Каким является тип или метод — открытым или закрытым

Универсальный тип или метод является закрытым, если все его параметры типа были заменены типами, в том числе все параметры типа для всех инкапсулирующих типов, были заменены типами, допускающими создание экземпляра. Если универсальный тип является закрытым, допускается только создание экземпляра на его основе. Если тип является открытым, свойство Type.ContainsGenericParameters возвращает значение true. Для методов та же самая функция выполняется методом MethodInfo.ContainsGenericParameters.

 

Создание закрытых универсальных типов

После получения определения универсального типа или метода следует при помощи метода MakeGenericType создать закрытый универсальный тип либо при помощи метода MakeGenericMethod – объект MethodInfo для закрытого универсального метода.

 

Получение определения универсального типа или метода

При наличии открытого универсального типа или метода, который не является определением универсального типа или метода нельзя создавать экземпляры такого типа или метода, а также указывать отсутствующие параметры типа. Необходимо иметь определение универсального типа или метода. Для получения определения универсального типа или метода GetGenericMethodDefinition используется метод GetGenericTypeDefinition.

Например, если при наличии объекта Type, представляющего тип Dictionary<int, string> (Dictionary(Of Integer, String) в Visual Basic) нужно создать тип Dictionary<string, MyClass>, можно при помощи метода GetGenericTypeDefinition получить объект Type, представляющий тип Dictionary<TKey, TValue>, а затем воспользоваться методом MakeGenericType для создания объекта Type, представляющий тип Dictionary<int, MyClass>.

Пример открытого универсального типа, который не является универсальным типом, см. в разделе "Параметр типа или аргумент типа" ниже в этой теме.

 

Изучение аргументов типа и параметров типа

Для получения массива объектов Type, которые представляют параметры типа или аргументы типа для универсального типа, следует воспользоваться методом Type.GetGenericArguments, а для выполнения тех же действий в отношении универсального метода — методом MethodInfo.GetGenericArguments.

Если станет понятно, что объект Type представляет параметр типа, есть много других дополнительных вопросов, на которые можно ответить при помощи отражения. Можно определить источник этого параметра типа, его положения и его ограничения.

 

Параметр типа или аргумент типа

Чтобы определить, является ли конкретный элемент массива параметром типа или аргументом типа, следует использовать свойство IsGenericParameter. Если элемент является параметром типа, свойство IsGenericParameter имеет значение true.

Универсальный тип может быть открытым, не являясь определением универсального типа. В таком случае он представляет собой набор аргументов типа и параметров типа. Так, в следующем параметре класс D является производным от типа, созданного путем замены первым параметром типа класса D вторым параметром типа класса B.

 

class B<T, U> {}

class D<V, W>: B<int, V> {}

 

В результате получения объекта Type, представляющего класс D<V, W>, и использования свойства BaseType для получения его базового типа, итоговый тип type B<int, V> является открытым, однако при этом не представляет собой определение универсального типа.

 

Источник универсального параметра

Параметр универсального типа может быть получен из изучаемого типа, инкапсулирующего типа или из универсального метода. Источник параметра универсального типа можно определить следующим образом:

Во-первых, можно воспользоваться свойством DeclaringMethod, чтобы определить, получен ли параметр типа из универсального метода. Если значение свойства не является пустой ссылкой (Nothing в Visual Basic), источник является универсальным методом.

Если источник не является универсальным методом, для определения универсального типа, которому принадлежит параметр универсального типа, следует использовать свойство DeclaringType.

Если параметр типа относится к универсальному методу, свойство DeclaringType возвращает тип, объявивший универсальный метод, который является несоответствующим.

 


Дата добавления: 2015-10-26; просмотров: 73 | Нарушение авторских прав


<== предыдущая страница | следующая страница ==>
Radio-Engineering| Сочинение пишите аккуратно, разборчивым почерком.

mybiblioteka.su - 2015-2024 год. (0.108 сек.)