Рефлексия (Reflection) – предоставление выполняемому коду информации о нем самом.
Рефлексия представляет собой процесс анализа типов (структуры типов) в ходе выполнения приложения (сборки). В .NET рефлексия реализуется свойствами и методами класса System.Type и классов пространства имен System.Reflection.
Пространство имен System.Reflection содержит классы и интерфейсы, которые позволяют организовать просмотр загруженных в сборку типов, методов, полей (данных-членов) и обеспечивают динамические механизмы реализации типов и вызова методов.
Включает множество взаимосвязанных классов, интерфейсов, структур и делегатов, предназначенных для реализации процесса отражения.
System.Reflection определяет типы для организации позднего связывания и динамической загрузки типов.
Неполный перечень классов представлен ниже:
| Тип | Назначение |
|---|---|
Assembly |
Методы для загрузки, описания и выполнения разнообразных операций над сборкой |
AssemblyName |
Информация о сборке (идентификатор, версия, язык реализации) |
EventInfo |
Информация о событиях |
FieldInfo |
Информация о полях |
MemberInfo |
Абстрактный базовый класс, определяющий общие члены для EventInfo, FieldInfo, MethodInfo, PropertyInfo |
MethodInfo |
Информация о методе |
Module |
Позволяет обратиться к модулю в многофайловой сборке |
ParameterInfo |
Информация о параметре |
PropertyInfo |
Информация о свойстве |
Класс System.Type содержит методы, позволяющие получать информацию о типах приложения. Является основой для реализации System.Reflection и средством для получения доступа к метаданным.
Использование членов класса Type позволяет получить информацию о:
GetType(string) );GetConstructors() );GetMethods() );GetFields() );GetProperties() );GetEvents() );Объект — представитель класса Type уникален. Две ссылки на объекты — представители класса Type оказываются эквивалентными, если только объекты были созданы в результате обращения к одному и тому же типу.
Объект — представитель класса Type может представить любой из следующих типов:
Ссылка на объект — представитель класса Type, ассоциированная с некоторым типом, может быть получена одним из следующих способов.
Type Object.GetType()
(метода — члена класса Object ), который возвращает ссылку на объект — представитель типа Type, представляющий информацию о типе. Вызов производится от имени объекта — представителя данного типа. Вызов становится возможен в связи с тем, что любой класс наследует тип Object.
Typepublic static Type Type.GetType(string)
параметром является строка со значением имени типа. Возвращает объект — представитель класса Type, с информацией о типе, специфицированном параметром метода.
Assembly — от имени объекта-сборки (самоописываемого, многократно используемого, версифицируемого БЛОКА (фрагмента) CLR-приложения) вызываются методыType[] Assembly.GetTypes()
Type Assembly.GetType(string)
// Получаем ссылку на сборку, содержащую объявление типа MyType,
// затем – массив объектов - представителей класса Type.
Type[] types = (Assembly.GetAssembly(typeof(MyType))).GetTypes();
// Без комментариев.
Type tt = (Assembly.GetAssembly(typeof(MyType))).GetType("MyType");
Здесь используется операция typeof. Операндом этой унарной операции является обозначение класса (в буквальном смысле – имя класса).
class MyType
{
:::::
}
:::::
Type t = typeof(MyType);
Module (модуль – portable executable файл с расширением .dll или .exe, состоящий из одного и более классов и интерфейсов):Type[] Module.GetTypes() Type Module.GetType(string) Type[] Module.FindTypes(TypeFilter filter, object filterCriteria)
где TypeFilter – класс-делегат.
typeof(), которая применяется для построения объекта-представителя класса System.Type. Выражение, построенное на основе операции typeof, имеет следующий вид:typeof(type)
Операнд выражения – тип, для которого может быть построен объект – представитель класса System.Type.
Пример применения операции:
using System;
using System.Reflection;
public class MyClass
{
public int intI;
public void MyMeth()
{
}
public static void Main()
{
Type t = typeof(MyClass);
// Альтернативная эквивалентная конструкция
// MyClass t1 = new MyClass();
// Type t = t1.GetType();
MethodInfo[] x = t.GetMethods();
foreach (MethodInfo m in x)
{
Console.WriteLine(m.ToString());
}
Console.WriteLine();
MemberInfo[] x2 = t.GetMembers();
foreach (MemberInfo m in x2)
{
Console.WriteLine(m.ToString());
}
}
}
Сначала – определения.
Раннее (
Позднее (динамическое) связывание – деятельность, выполняемая непосредственно при выполнении приложения и позволяющая:
При этом вызов методов и свойств при выполнении приложения обеспечивается методом InvokeMember. Этот метод выполняет достаточно сложную работу и поэтому нуждается в изощренной системе управления, для реализации которой применяется перечисление BindingFlags. В рамках этого перечисления определяются значения флажков, которые управляют процессом динамического связывания в ходе реализации отражения.
Перечисление также применяется для управления методом GetMethod.
Список элементов перечисления прилагается.
| Имя элемента | Описание |
|---|---|
CreateInstance |
Определяет, что отражение должно создавать экземпляр заданного типа. Вызывает конструктор, соответствующий указанным аргументам. Предоставленное имя пользователя не обрабатывается. Если тип поиска не указан, будут использованы флаги (Instance | Public). Инициализатор типа вызвать нельзя |
DeclaredOnly |
Определяет, что должны рассматриваться только члены, объявленные на уровне переданной иерархии типов. Наследуемые члены не учитываются |
Default |
Определяет отсутствие флагов связывания |
ExactBinding |
Определяет, что типы представленных аргументов должны точно соответствовать типам соответствующих формальных параметров. Если вызывающий оператор передает непустой объект BindToXXX, которые выберут соответствующий метод |
| ChangeType | Отражение моделирует правила доступа для системы общих типов. Например, если вызывающий оператор находится в той же сборке, ему не нужны специальные разрешения относительно внутренних членов. В противном случае вызывающему оператору потребуется ReflectionPermission. Этот метод применяется при поиске защищенных, закрытых и т. п. членов.Главный принцип заключается в том, что Связыватель по умолчанию не обрабатывает этот флаг, но пользовательские связыватели используют семантику этого флага |
FlattenHierarchy |
Определяет, что должны быть возвращены статические члены вверх по иерархии. Статические члены — это поля, методы, события и свойства. |
GetField |
Определяет, что должно возвращаться значение указанного поля |
GetProperty |
Определяет, что должно возвращаться значение указанного свойства |
IgnoreCase |
Определяет, что при связывании не должен учитываться регистр имени члена |
IgnoreReturn |
Используется при COM-взаимодействии для определения того, что возвращаемое значение члена может быть проигнорировано |
Instance |
Определяет, что в поиск должны быть включены члены экземпляра |
InvokeMethod |
Определяет, что метод должен быть вызван. Метод не может быть ни конструктором, ни инициализатором типа |
NonPublic |
Определяет, что в поиск должны быть включены члены экземпляра, не являющиеся открытыми ( public ) |
OptionalParamBinding |
Возвращает набор членов, у которых количество параметров соответствует количеству переданных аргументов. Флаг связывания используется для методов с параметрами, у которых есть значения методов, и для функций с переменным количеством аргументов (Type.InvokeMember.Параметры со значениями по умолчанию используются только в тех вызовах, где опущены конечные аргументы. Они должны быть последними аргументами |
Public |
Определяет, что открытые ( public ) члены должны быть включены в поиск |
PutDispProperty |
Определяет, что для COM-объекта должен быть вызван член PROPPUT. PROPPUT задает устанавливающую свойство функцию, использующую значение. Следует использовать PutDispProperty, если для свойства заданы и PROPPUT, и PROP-PUTREF и нужно различать вызываемые методы |
PutRefDispProperty |
Определяет, что для COM-объекта должен быть вызван член PROPPUTREF. PROPPUTREF использует устанавливающую свойство функцию, использующую ссылку, вместо значения. Следует использовать PutRefDispProperty, если для свойства заданы и PROPPUT, и PROPPUTREF и нужно различать вызываемые методы |
SetField |
Определяет, что должно устанавливаться значение указанного поля |
SetProperty |
Определяет, что должно устанавливаться значение указанного свойства. Для COM-свойств задание этого флага связывания эквивалентно заданию PutDispProperty и PutRefDispProperty |
Static |
Определяет, что в поиск должны быть включены статические члены |
SuppressChangeType |
Не реализован |
Далее демонстрируется применение класса Type, в частности варианты использования метода – члена класса Type InvokeMember, который обеспечивает выполнения методов и свойств класса.
using System;
using System.Reflection;
// В классе объявлены поле myField, конструктор, метод String ToString(), свойство.
class MyType
{
int myField;
public MyType(ref int x)
{
x *= 5;
}
public override String ToString()
{
Console.WriteLine("This is: public override String ToString() method!");
return myField.ToString();
}
// Свойство MyProp нашего класса обладает одной замечательной особенностью:
// значение поля myField объекта - представителя класса MyType не может
// быть меньше нуля. Если это ограничение нарушается – возбуждается
// исключение.
public int MyProp
{
get
{
return myField;
}
set
{
if (value < 1)
throw new ArgumentOutOfRangeException("value", value, "value must be > 1");
myField = value;
}
}
}
class MyApp
{
static void Main()
{
// Создали объект - представитель класса Type
// на основе объявления класса MyType.
Type t = typeof(MyType);
// А это одномерный массив объектов, содержащий ОДИН элемент.
// В этом массиве будут передаваться параметры конструктору.
Object[] args = new Object[] {8};
Console.WriteLine("The value of x before the constructor is called is {0}.", args[0]);
// Вот таким образом в рамках технологии отражения производится
// обращение к конструктору. Наш объект адресуется по ссылке obj.
Object obj = t.InvokeMember(
null,
//____________________________
BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.CreateInstance, // Вот распоряжение о создании объекта...
//____________________________
null,
null,
args // А так организуется передача параметров в конструктор.
);
Console.WriteLine("Type: " + obj.GetType().ToString());
Console.WriteLine("The value of x after the constructor returns is {0}.", args[0]);
// Изменение (запись и чтение) значения поля myField только что
// созданного объекта - представителя класса MyType.
// Как известно, этот объект адресуется по
// ссылке obj. Мы сами его по этой ссылке расположили!
t.InvokeMember(
"myField", // Будем менять значение поля myField...
//______________________________
BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.SetField, // Вот инструкция по изменению значения поля.
//_______________________________
null,
obj, // Вот указание на то, ГДЕ располагается объект...
new Object[] {5} // А вот и само значение. Оно упаковывается в массив объектов.
);
int v = (Int32) t.InvokeMember(
"myField",
//______________________________
BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.GetField, // А сейчас мы извлекаем значение поля myField.
//______________________________
null,
obj, // "Работаем" все с тем же объектом. Значение поля myField
// присваивается переменной v.
null
);
// Вот распечатали это значение.
Console.WriteLine("myField: " + v);
// "От имени" объекта будем вызывать нестатический метод.
String s = (String) t.InvokeMember(
"ToString", // Имя переопределенного виртуального метода.
//______________________________
BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.InvokeMethod, // Сомнений нет! Вызываем метод!
//______________________________
null,
obj, // От имени нашего объекта вызываем метод без параметров.
null
);
// Теперь обращаемся к свойству.
Console.WriteLine("ToString: " + s);
// Изменение значения свойства. Пытаемся присвоить недозволенное
// значение. И посмотрим, что будет...
// В конце концов, мы предусмотрели перехватчик исключения.
try
{
t.InvokeMember(
"MyProp", // Работаем со свойством.
//______________________________
BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.SetProperty, // Установить значение свойства.
//______________________________
null,
obj,
new Object[] {0} // Пробуем через обращение к свойству
// установить недозволенное значение.
);
}
catch (TargetInvocationException e)
{
// Фильтруем исключения... Реагируем только на исключения типа
// ArgumentOutOfRangeException. Все остальные "проваливаем дальше".
if (e.InnerException.GetType() !=typeof (ArgumentOutOfRangeException)) throw;
// А вот как реагируем на ArgumentOutOfRangeException.
// Вот так скромненько уведомляем о попытке присвоения запрещенного
// значения.
Console.WriteLine("Exception! Catch the property set.");
}
t.InvokeMember(
"MyProp",
//______________________________
BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.SetProperty, // Установить значение свойства.
//______________________________
null,
obj,
new Object[] {2} // Вновь присваиваемое значение. Теперь ПРАВИЛЬНОЕ.
);
v = (int) t.InvokeMember(
"MyProp",
BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.GetProperty, // Прочитать значение свойства.
null,
obj,
null
);
Console.WriteLine("MyProp: " + v);
}
}
Ну вот. Создавали объект, изменяли значение его поля (данного-члена), вызывали его (нестатический) метод, обращались к свойству (подсовывали ему некорректные значения). И при этом НИ РАЗУ НЕ НАЗЫВАЛИ ВЕЩИ СВОИМИ ИМЕНАМИ! В сущности, ЭТО И ЕСТЬ ОТРАЖЕНИЕ.
Атрибут – средство добавления ДЕКЛАРАТИВНОЙ информации к элементам программного кода. Назначение атрибутов – внесение всевозможных не предусмотренных обычным ходом выполнения приложения изменений:
Эта декларативная информация составляет часть метаданных кода. Она может быть использована при помощи механизмов отражения.
Структура атрибута регламентирована. Атрибут – это класс. Общий предок всех атрибутов – класс System.Attribute.
Информация, закодированная с использованием атрибутов, становится доступной в процессе ОТРАЖЕНИЯ (рефлексии типов).
Атрибуты типизированы.
.NET способна прочитать информацию в атрибутах и использовать ее в соответствии с предопределенными правилами или замыслами разработчика. Различаются:
DllImport – для загрузки .dll-файлов;
Serializable – означает возможность сериализации свойств объекта – представителя класса;
NonSerialized – обозначает данные-члены класса как несериализуемые. Карандаши (средство графического представления информации, элемент GDI+) не сериализуются;
В основе пользовательских атрибутов – все та же система типов с наследованием от базового класса System.Attribute.
И неспроста! В конце концов, информация, содержащаяся в атрибутах, предназначается для CLR, и она в случае необходимости должна суметь разобраться в этой информации. Пользователи или другие инструментальные средства должны уметь кодировать и декодировать эту информацию.
Добавлять атрибуты можно к:
Ниже приводится описание членов класса Attribute.
TypeId |
При реализации в производном классе получает уникальный идентификатор для этого атрибута Attribute |
Equals |
Переопределен (см. Object.Equals ) |
GetCustomAttribute |
Перегружен. Извлекает пользовательский атрибут указанного типа, который применен к заданному члену класса |
GetCustomAttributes |
Перегружен. Извлекает массив пользовательских атрибутов указанного типа, которые применены к заданному члену класса |
GetHashCode |
Переопределен. Возвращает хэш-код для этого экземпляра |
GetType (унаследовано от Object ) |
Возвращает Type текущего экземпляра |
IsDefaultAttribute |
При переопределении в производном классе возвращает значение, показывающее, является ли значение этого производного экземпляра значением по умолчанию для производного класса |
IsDefined |
Перегружен. Определяет, применены ли какие-либо пользовательские атрибуты заданного типа к указанному члену класса |
Match |
При переопределении в производном классе возвращает значение, указывающее, является ли этот экземпляр эквивалентным заданному объекту |
ToString (унаследовано от Object ) |
Возвращает String, который представляет текущий Object |
Attribute -конструктор |
Инициализирует новый экземпляр класса Attribute |
Finalize (унаследовано от Object ) |
Переопределен. Позволяет объекту Object попытаться освободить ресурсы и выполнить другие завершающие операции, перед тем как объект Object будет уничтожен в процессе сборки мусора.В C# для функций финализации используется синтаксис деструктора |
MemberwiseClone (унаследовано от Object ) |
Создает неполную копию текущего Object |
Следующий пример является демонстрацией объявления и применения производных атрибутов:
using System;
using System.Reflection;
namespace CustomAttrCS
{
// Перечисление of animals.
// Start at 1 (0 = uninitialized).
public enum Animal
{
// Pets.
Dog = 1,
Cat,
Bool,
}
// Перечисление of colors.
// Start at 1 (0 = uninitialized).
public enum Color
{
// Colors.
Red = 1,
Brown,
White,
}
// Класс пользовательских атрибутов.
public class AnimalTypeAttribute : Attribute
{//==============================================================
// Данное - член типа "перечисление".
protected Animal thePet;
protected string WhoIs(Animal keyPet)
{
string retStr = "";
switch (keyPet)
{
case Animal.Dog: retStr = "This is the Dog!"; break;
case Animal.Cat: retStr = "This is the Cat!"; break;
case Animal.Bull: retStr = "This is the Bool!"; break;
default: retStr = "Unknown animal!"; break;
}
return retStr;
}
// Конструктор вызывается при установке атрибута.
public AnimalTypeAttribute(Animal pet)
{
thePet = pet;
Console.WriteLine("{0}", WhoIs(pet));
}
// Свойство, демонстрирующее значение атрибута.
public Animal Pet
{
get
{
return thePet;
}
set
{
thePet = value;
}
}
}//==============================================================
// Еще один класс пользовательских атрибутов.
public class ColorTypeAttribute : Attribute
{//==============================================================
// Данное - член типа "перечисление".
protected Color theColor;
// Конструктор вызывается при установке атрибута.
public ColorTypeAttribute(Color keyColor)
{
theColor = keyColor;
}
// Свойство, демонстрирующее значение атрибута.
public Color ColorIs
{
get
{
return theColor;
}
set
{
theColor = ColorIs;
}
}
}//==============================================================
// A test class where each method has its own pet.
class AnimalTypeTestClass
{//==============================================================
// Содержит объявления трех методов, каждый из которых
// предваряется соответствующим ПОЛЬЗОВАТЕЛЬСКИМ атрибутом.
// У метода может быть не более одного атрибута данного типа.
[AnimalType(Animal.Dog)]
[ColorType(Color.Brown)]
public void DogMethod()
{
Console.WriteLine("This is DogMethod()...");
}
[AnimalType(Animal.Cat)]
public void CatMethod()
{
Console.WriteLine("This is CatMethod()...");
}
[AnimalType(Animal.Bool)]
[ColorType(Color.Red)]
public void BoolMethod(int n, string voice)
{
int i;
Console.WriteLine("This is BoolMethod!");
if (n > 0) for (i = 0; i < n; i++)
{
Console.WriteLine(voice);
}
}
}//==============================================================
class DemoClass
{//==============================================================
static void Main(string[] args)
{
int invokeFlag;
int i;
// И вот ради чего вся эта накрутка производилась...
// Объект класса AnimalTypeTestClass под именем testClass
// представляет собой КОЛЛЕКЦИЮ методов, каждый из которых
// снабжен соответствующим ранее определенным пользовательским
// СТАНДАРТНЫМ атрибутом. У класса атрибута AnimalTypeAttribute есть все,
// что положено иметь классу, включая конструктор.
AnimalTypeTestClass testClass = new AnimalTypeTestClass();
// Так вот создали соответствующий объект - представитель класса...
// Объект - представитель класса сам может служить источником
// информации о собственном классе. Информация о классе представляется
// методом GetType() в виде
// объекта - представителя класса Type. Информационная капсула!
Type type = testClass.GetType();
// Из этой капсулы можно извлечь множество всякой "полезной" информации...
// Например, можно получить коллекцию (массив) элементов типа MethodInfo
// (описателей методов), которая содержит список описателей методов,
// объявленных в данном классе. В список будет включена информация
// о ВСЕХ методах класса: о тех, которые были определены явным
// образом, и о тех, которые были унаследованы
// от базовых классов. И по этому списку описателей методов мы
// пройдем победным маршем ("Ha-Ha-Ha") оператором foreach.
i = 0;
foreach(MethodInfo mInfo
in
type.GetMethods())
{
invokeFlag = 0;
Console.WriteLine("#####{0}#####{1}#####", i, mInfo.Name);
// И у каждого из методов мы спросим относительно множества атрибутов,
// которыми метод был снабжен при объявлении класса.
foreach (Attribute attr
in
Attribute.GetCustomAttributes(mInfo))
{
Console.WriteLine("~~~~~~~~~~");
// Check for the AnimalType attribute.
if (attr.GetType() == typeof(AnimalTypeAttribute))
{
Console.WriteLine("Method {0} has a pet {1} attribute.",
mInfo.Name,
((AnimalTypeAttribute)attr).Pet);
// Посмотрели значение атрибута – и если это Animal.Bool – подняли флажок.
if (((AnimalTypeAttribute)attr).Pet.CompareTo(Animal.Bool) == 0)
invokeFlag++;
}
if (attr.GetType() == typeof(ColorTypeAttribute))
{
Console.WriteLine("Method {0} has a color {1} attribute.",
mInfo.Name,
((ColorTypeAttribute)attr).ColorIs);
// Посмотрели значение атрибута – и если это Color.Red –
// подняли флажок второй раз.
if (((ColorTypeAttribute)attr).ColorIs.CompareTo(Color.Red) == 0)
invokeFlag++;
}
// И если случилось счастливое совпадение значений атрибутов метода
// (Красный Бычок), то метод выполняется.
// Метод Invoke в варианте с двумя параметрами:
// объект - представитель исследуемого класса
// (в данном случае AnimalTypeTestClass), и массив объектов-параметров.
if (invokeFlag == 2)
{
object[] param = {5,"Mmmuuu–uu–uu!!! Mmm..."};
mInfo.Invoke(new AnimalTypeTestClass(),param);
}
Console.WriteLine("~~~~~~~~~~");
}
Console.WriteLine("#####{0}#####", i);
i++;
}
}
}//==============================================================
}
Эта программа демонстрирует одну замечательную особенность синтаксиса объявления атрибутов. При их применении можно использовать сокращенные имена ранее объявленных классов атрибутов.
В программе объявлено два класса атрибутов – наследников класса Attribute:
public class AnimalTypeAttribute : Attribute... public class ColorTypeAttribute : Attribute...
При объявлении соответствующих экземпляров атрибутов последняя часть имени класса атрибута ( ...Attribute ) была опущена.
Вместо
[AnimalTypeAttribute(Animal.Dog)]
используется имя
[AnimalType(Animal.Dog)],
вместо
[ColorTypeAttribute(Color.Red)]
используется
[ColorType(Color.Red)]
Транслятор терпимо относится только к этой модификации имени класса атрибута. Любые другие изменения имен атрибутов пресекаются.
Класс Assembly определяет Сборку – основной строительный блок Common Language Runtime приложения. Как строительный блок CLR, сборка обладает следующими основными свойствами:
Эти понятия являются ключевыми для решения проблемы отслеживания версии и для упрощения развертывания приложений во время выполнения.
Сборки обеспечивают инфраструктуру, которая позволяет во время выполнения полностью "понимать" структуру и содержимое приложения и контролировать версии и зависимости элементов выполняемого приложения.
Сборки бывают:
Загружаемая сборка строится как БИБЛИТОТЕКА КЛАССОВ (файл с расширением .dll), либо как выполняемый модуль (файл с расширением .exe).
Если это файл с расширением .dll, то в среде Visual Studio ее использование поддерживается специальными средствами среды. Это "полуавтоматическая" загрузка частной сборки в Reference приложения (Add Reference...). Сборки, располагаемые в .exe-файлах, особой поддержкой для включения сборки в состав приложения не пользуются.
Для анализа сборки применяется утилита IlDasm.exe, которую можно подключить к непосредственно вызываемому из среды разработки Visual Studio списку утилит.
Ниже представлены члены класса сборки.
CodeBase |
Возвращает местонахождение сборки, указанное первоначально, например в объекте AssemblyName |
EntryPoint |
Возвращает точку входа для этой сборки |
EscapedCodeBase |
Возвращает URI, предоставляющий базовый код, включая escape-знаки |
Evidence |
Возвращает свидетельство для этой сборки |
FullName |
Возвращает отображаемое имя сборки |
GlobalAssemblyCache |
Возвращает значение, показывающее, была ли сборка загружена из глобального кэша сборок |
ImageRuntimeVersion |
Возвращает версию общеязыковой среды выполнения (CLR), сохраненной в файле, который содержит манифест |
Location |
Возвращает местонахождение в формате базового кода загруженного файла, содержащего манифест, если для него не было теневого копирования |
CreateInstance |
Перегружен. Находит тип в этой сборке и создает его экземпляр, используя абстрактный метод |
CreateQualifiedName |
Статический. Создает тип, задаваемый отображаемым именем его сборки |
Equals (унаследовано от Object ) |
Перегружен. Определяет, равны ли два экземпляра Object |
GetAssembly |
Статический. Возвращает сборку, в которой определяется заданный класс |
GetCallingAssembly |
Статический. Возвращает Assembly метода, который вызывает текущий метод выполнения |
GetCustomAttributes |
Перегружен. Возвращает пользовательские атрибуты для этой сборки |
GetEntryAssembly |
Статический. Возвращает процесс, исполняемый в AppDomain.ExecuteAssembly |
GetExecutingAssembly |
Статический. Возвращает Assembly, из которой исполняется текущий код |
GetExportedTypes |
Возвращает экспортируемые типы, определенные в этой сборке |
GetFile |
Возвращает объект FileStream для указанного файла из таблицы файлов манифеста данной сборки |
GetFiles |
Перегружен. Возвращает файлы в таблице файлов манифеста сборки. |
GetHashCode (унаследовано от Object ) |
Служит хэш-функцией для конкретного типа, пригоден для использования в алгоритмах хэширования и структурах данных, например в хэш-таблице |
GetLoadedModules |
Перегружен. Возвращает все загруженные модули, являющиеся частью этой сборки |
GetManifestResourceInfo |
Возвращает информацию о способе сохранения данного ресурса |
GetManifestResourceNames |
Возвращает имена всех ресурсов в этой сборке |
GetManifestResourceStream |
Перегружен. Загружает указанный ресурс манифеста из сборки |
GetModule |
Возвращает указанный модуль этой сборки |
GetModules |
Перегружен. Возвращает все модули, являющиеся частью этой сборки |
GetName |
Перегружен. Возвращает AssemblyName для этой сборки |
GetObjectData |
Возвращает сведения сериализации со всеми данными, необходимыми для повторного создания этой сборки |
GetReferencedAssemblies |
Возвращает объекты AssemblyName для всех сборок, на которые ссылается данная сборка |
GetSatelliteAssembly |
Перегружен. Возвращает сопутствующую сборку |
GetType |
Перегружен. Возвращает объект Type, предоставляющий указанный тип |
GetTypes |
Возвращает типы, определенные в этой сборке |
IsDefined |
Показывает, определен ли пользовательский атрибут, заданный указанным значением Type |
Load |
Статический. Перегружен. Загружает сборку |
LoadFile |
Статический. Перегружен. Загружает содержимое файла сборки |
LoadFrom |
Статический. Перегружен. Загружает сборку |
LoadModule |
Перегружен. Загружает внутренний модуль этой сборки |
LoadWithPartialName |
Статический. Перегружен. Загружает сборку из папки приложения или из глобального кэша сборок, используя частичное имя |
ToString |
Переопределен. Возвращает полное имя сборки, также называемое отображаемым именем |
ModuleResolve |
Возникает, когда загрузчик классов общеязыковой среды выполнения не может обработать ссылку на внутренний модуль сборки, используя обычные средства |
Finalize (унаследовано от Object ) |
Переопределен. Позволяет объекту Object попытаться освободить ресурсы и выполнить другие завершающие операции, перед тем как объект Object будет уничтожен в процессе сборки мусора.В языках C# и C++ для функций финализации используется синтаксис деструктора |
MemberwiseClone (унаследовано от Object ) |
Создает неполную копию текущего Object |
Исследование свойств и областей применения класса Assembly начинаем с создания тестовой однофайловой сборки AssemblyForStart в рамках проекта Class Library.
Первая сборка Operators00.exe:
using System;
namespace Operators00
{
public class xPoint
{
float x, y;
xPoint()
{
x = 0.0F;
y = 0.0F;
}
public xPoint(float xKey, float yKey):this()
{
x = xKey;
y = yKey;
}
public static bool operator true(xPoint xp)
{
if (xp.x != 0.0F xp.y != 0.0F) return true;
else return false;
}
public static bool operator false(xPoint xp)
{
if (xp.x == 0.0F || xp.y == 0.0F) return false;
else return true;
}
public static xPoint operator | (xPoint key1, xPoint key2)
{
if (key1) return key1;
if (key2) return key2;
return new xPoint();
}
public static xPoint operator (xPoint key1, xPoint key2)
{
if (key1 key2) return new xPoint(1.0F, 1.0F);
return new xPoint();
}
public void Hello()
{
Console.WriteLine("Hello! Point {0},{1} is here!",this.x,this.y);
}
}
class Class1
{
// The main entry Point for the application.
static void Main() // У точки входа пустой список параметров.
// Я пока не сумел ей передать через метод Invoke массива строк.
{
xPoint xp0 = new xPoint(1.0F, 1.0F);
xPoint xp1 = new xPoint(1.0F, 1.0F);
if (xp0 || xp1) Console.WriteLine("xp0 || xp1 is true!");
else Console.WriteLine("xp0 || xp1 is false!");
}
}
}
Вторая сборка AssemblyForStart.dll. В примере она так и не запускалась. Используется только для тестирования стандартных средств загрузки сборок – библиотек классов:
using System;
namespace AssemblyForStart
{
// Class1 : первая компонента сборки AssemblyForStart.
public class Class1
{
public Class1()
{
Console.WriteLine("This is constructor Class1()");
}
public void fC1()
{
Console.WriteLine("This is fC1()");
}
}
}
А вот полигон AssemblyStarter.exe. В примере демонстрируется техника ПОЗДНЕГО СВЯЗЫВАНИЯ. Именно поэтому код, который выполняется после загрузки сборки, не содержит в явном виде информации об используемых в приложении типах:
using System;
using System.Reflection;
using System.IO;
namespace AssemblyStarter
{
// Приложение обеспечивает запуск сборки.
class Class1
{
static void Main(string[] args)
{
// Сборка может быть вызвана непосредственно по имени
// (строковый литерал с дружественным именем сборки).
// Ничего особенного. Просто имя без всяких там расширений.
// Главная проблема заключается в том, что сборки должны
// предварительно включаться в раздел References (Ссылки).
// Кроме того, информация о загружаемой сборке может быть представлена
// в виде объекта - представителя класса AssemblyName, ссылка
// на который также может быть передана в качестве аргумента методу
// Assembly.Load().
// Вот здесь как раз и происходит формирование этого самого объекта.
AssemblyName asmName = new AssemblyName();
asmName.Name = "AssemblyForStart";
// Версию подсмотрели в манифесте сборки с помощью IlDasm.exe.
Version v = new Version("1.0.1790.25124");
// Можно было бы для пущей крутизны кода поле Version
// проинициализировать непосредственно (дело хозяйское):
// asmName.Version = new Version("1:0:1790:25124");
asmName.Version = v;
// Ссылка на объект - представитель класса Assembly.
//
Assembly asm = null;
try
{
// Загрузка сборки по "дружественному имени".
//asm = Assembly.Load("AssemblyForStart");
// Путь и полное имя при загрузке частной сборки не имеют значения.
// Соответствующие файлы должны располагаться непосредственно
// в каталоге приложения.
//asm = Assembly.Load
//(@"D:\Users\WORK\Cs\AssemblyTest\AssemblyForStart\bin\Debug\ AssemblyForStart.dll");
//asm = Assembly.Load(asmName);
// Если сборку организовать в виде исполняемого модуля и
// "запихнуть" в каталог вручную – загрузится и такая сборка.
asm = Assembly.Load("Operators00");
}
catch(FileNotFoundException e)
{
Console.WriteLine("We have a problem:" + e.Message);
}
// Итак, решено. Загрузили сборку, содержащую объявление класса xPoint.
// Если сборка загрузилась – с ней надо что-то делать. Ясное дело,
// надо выполнять программный код сборки.
// Первый вариант выполнения. В сборке содержатся объявления двух классов:
// класса xPoint и класса Class1. Мы воспользуемся объявлением класса
// xPoint, построим соответствующий объект - представитель этого класса,
// после чего будем вызывать нестатические методы - члены этого класса.
// Все происходит в режиме ПОЗДНЕГО связывания.
// Поэтому ни о каких ЯВНЫХ упоминаниях
// имен типов не может быть речи.
// Единственное явное упоминание – это упоминание
// имени метода.
object[] ps = {25,25};
Type[] types = asm.GetTypes();
// Здесь используется класс Activator!
object obj = Activator.CreateInstance(types[0],ps);
MethodInfo mi = types[0].GetMethod("Hello");
mi.Invoke(obj,null);
// Второй вариант выполнения. Воспользуемся тем обстоятельством,
// что загружаемая сборка не является библиотекой классов, а является
// обычной выполнимой сборкой с явным образом обозначенной точкой
// входа – СТАТИЧЕСКОЙ функцией Main(), которая является членом
// класса Class1.
mi = asm.EntryPoint;
// Вот, все получилось! Единственное, что я не сделал,
// так это не смог передать в точку входа загруженной
// сборки массива строк-параметров (смотреть на точку входа данной
// сборки). Ну не удалось. Потому и в сборке
// Operators точку входа сборки объявил без параметров.
mi.Invoke(null,null);
}
}
}
Собрали, запустили. Получилось. Стало быть, ВСЕ ХОРОШО.
В ходе выполнения приложения класс Assembly позволяет:
В приведенном выше примере демонстрируется техника ПОЗДНЕГО СВЯЗЫВАНИЯ. Именно поэтому код, который выполняется после загрузки сборки, не содержит в явном виде информации об используемых в приложении типах. Транслятор действует в строгом соответствии с синтаксисом языка C# и просто не поймет пожелания "создать объект – представитель класса ..., который будет объявлен в сборке, которую предполагается загрузить в ходе выполнения приложения".
Класс Activator – главное средство, обеспечивающее позднее связывание.
Содержит методы, позволяющие создавать объекты на основе информации о типах, получаемой непосредственно в ходе выполнения приложения, а также получать ссылки на существующие объекты. Далее приводится список перегруженных статических методов класса:
CreateComInstanceFrom. Создает экземпляр COM-объекта;CreateInstance. Создает объект – представитель specified-типа, используя при этом наиболее подходящий по списку параметров конструктор (best matches the specified parameters).Пример:
ObjectHandle hdlSample;
IMyExtenderInterface myExtenderInterface;
string argOne = "Value of argOne";
int argTwo = 7;
object[] args = {argOne, argTwo};
// Uses the UrlAttribute to create a remote object.
object[] activationAttributes =
{new UrlAttribute("http://localhost:9000/MySampleService")};
// Activates an object for this client.
// You must supply a valid fully qualified assembly name here.
hdlSample = Activator.CreateInstance(
"Assembly text name, Version, Culture, PublicKeyToken",
"samplenamespace.sampleclass",
true,
BindingFlags.Instance|BindingFlags.Public,
null,
args,
null,
activationAttributes,
null);
myExtenderInterface = (IMyExtenderInterface)hdlSample.Unwrap();
Console.WriteLine(myExtenderInterface.SampleMethod("Bill"));
CreateInstanceFrom. Создает объект – представитель типа, специфицированного по имени. Имя специфицируется на основе имени сборки. При этом используется подходящий по списку параметров конструктор (the constructor that best matches the specified parameters).Пример:
ObjectHandle hdlSample;
IMyExtenderInterface myExtenderInterface;
object[] activationAttributes = {new SynchronizationAttribute()};
// Assumes that SampleAssembly.dll exists in the same directory as this assembly.
hdlSample = Activator.CreateInstanceFrom(
"SampleAssembly.dll",
"SampleNamespace.SampleClass",
activationAttributes);
// Assumes that the SampleClass implements an interface provided by
// this application.
myExtenderInterface = (IMyExtenderInterface)hdlSample.Unwrap();
Console.WriteLine(myExtenderInterface.SampleMethod("Bill"));
GetObject – Сборка может быть вызвана непосредственно по имени (строковый литерал с дружественным именем сборки).
Ничего особенного. Просто имя без всяких там расширений. Главная проблема заключается в том, что частные сборки должны предварительно включаться в раздел References (Ссылки).
Кроме того, информация о загружаемой сборке может быть представлена в виде объекта – представителя класса AssemblyName, ссылка на который также может быть передана в качестве аргумента методу Assembly.Load().
Эта характеристика имеется у каждой сборки, несмотря на то, что частной сборке она и ни к чему.
Версию можно "подсмотреть" в манифесте сборки с помощью IlDasm.exe. Можно было бы для пущей крутизны кода поле Version проинициализировать непосредственно (дело хозяйское).
И тут открываются несколько проблем:
Version эти цифры следует разделять точкой;Version можно спокойно забивать любые четверки чисел. Лишь бы требования формата были соблюдены;Следующий шаг: загружаем сборку в память.
Используется блок try, поскольку загружаемую сборку можно и не найти.
При загрузке сборки известно ее расположение (application directory), однако с расширением имени могут возникнуть проблемы. Действует такой алгоритм "поиска" (и называть-то это поиском как-то неудобно):
Среда выполнения .NET пытается обнаружить файл с расширением .dll.
В случае неудачи среда выполнения .NET пытается обнаружить файл с расширением .exe.
В случае неудачи – предпринимаются ДРУГИЕ алгоритмы поиска.
Так что за ДРУГИЕ алгоритмы?
Все зависит от файла конфигурации приложения. В этом файле можно явным образом сформулировать особенности приложения. В частности, явным образом указать место расположения загружаемой сборки.
Файл конфигурации – это текстовый файл со странным именем <ИмяФайла.Расширение>.config, в котором размещаются в строго определенном порядке теги, прописанные на языке XML.
Среда выполнения .NET умеет читать XML.
Утверждается, что если расположить частные сборки в других подкаталогах приложения, нежели bin\Debug, то с помощью файла конфигурации эти сборки исполняющая среда БЕЗ ТРУДА обнаружит. Однако не находит. Может быть, я что-то не так делаю?
Файл AssemblyStarter.exe.config: <configuration> <runtime> <assemblyBinding xmlns="urn:schemas–microsoft–com:asm.v1" > <probing privatePath="XXX\YYY"/> </assemblyBinding> </runtime> </configuration>
Строгое имя общедоступной сборки состоит из:
Делаем общую сборку.
using System;
namespace SharedAssembly00
{
// Summary description for Class1.
public class Class1
{
public Class1()
{
}
public void f0()
{
Console.WriteLine("This is SharedAssembly00.f0()");
}
}
}
Для этого в Visual Studio .NET 2003 Command Prompt командной строкой вызываем утилиту – генератор ключей:
D:\...>sn –k theKey.cnk
AssemblyKeyFile полный путь к созданному утилитой sn файлу (в одну строчку):[assembly: AssemblyKeyFile(@"D:\Users\WORK\Cs\AssemblyTest\Shared Assembly00\theKey.snk")]
| Клиентское приложение | Сборка общего пользования, установленная в GAC |
|---|---|
В манифесте клиента имеется ссылка на внешнюю сборку общего пользования. Маркер открытого ключа этой сборки отмечен тегом:::::::::::
.assembly extern
SharedAssembly00
{
.publickeytoken =
(90 8E D8 5E 3E 37 72 08)// ...^>7r.
.ver 1:0:1790:37888
}
::::::::::
|
Манифест сборки общего пользования в GAC содержит такое же значение ключа:908ED85E3E377208 Его можно увидеть при исследовани и содержимого GAC (свойства элемента) |
| А закрытый ключ сборки общего пользования совместно с открытым ключом используется для создания цифровой подписи сборки и хранится вместе с подписью в самой сборке |
\i и именем сборки с полным путем в качестве второго параметра, либо просто перетаскиваем мышкой файл сборки в каталог, содержащий GAC. В случае успеха наблюдаем состояние GAC.Сборка – там!
Создаем новую сборку, в которой предполагается использовать наше детище. Затем добавляем ссылку на сборку. Не все так просто. Сначала надо отыскать соответствующую .dll'ку. Наличие ключа в манифесте сборки приводит к тому, что сборка (в отличие от частных сборок) не будет копироваться в каталог запускающей сборки. Вместо этого исполняющая среда будет обращаться в GAC. Дальше – проще.
Объявленные в сборке классы оказываются доступны запускающей сборке. Набрали аж 3 сборки общего пользования. Дело нехитрое...
using System;
using SharedAssembly00;
using SharedAssembly01;
using SharedAssembly02;
namespace AssemblyStarter01
{
// Summary description for Class1.
class startClass
{
// The main entry Point for the application.
static void Main(string[] args)
{
try
{
SharedAssembly00.Class1 c001 = new SharedAssembly00.Class1(); c001.f0();
SharedAssembly01.Class1 c011 = new SharedAssembly01.Class1(); c011.f0();
SharedAssembly02.Class1 c021 = new SharedAssembly02.Class1(); c021.f0();
}
catch(TypeLoadException e)
{
Console.WriteLine("We are the problem: " + e.Message);
}
}
}
}
Итак, подключили общедоступную сборку. Она не копируется, а остается в GAC. При создании клиента были выполнены определенные телодвижения, в результате которых клиент сохраняет информацию о свойствах располагаемых в GAC компонентов. Свойства этих компонент можно посмотреть после подсоединения данного элемента из GAC к References клиента. В частности, там есть свойство Copy local (по умолчанию установленное в false ). Это означает, что соответствующая компонента из GAC клиентом не копируется. Общую сборку можно превратить в частную сборку, если это свойство установить в true.
Все, о чем писалось до этого момента, – суть СТАТИЧЕСКИЕ СБОРКИ. Статические сборки существуют в виде файлов на диске или других носителях, в случае необходимости загружаются в оперативную память и выполняются благодаря функциональным возможностям класса Assembly.
Динамические сборки создаются непосредственно в ходе выполнения приложения (статической сборки) и существуют в оперативной памяти. По завершении выполнения приложения они обречены на бесследное исчезновение, если, конечно, не были предприняты особые усилия по их сохранению на диск.
Для работы с динамическими сборками используется пространство имен System.Reflection.Emit.
Emit – излучать, испускать, выпускать (деньги).
Это множество типов позволяет создавать и выполнять динамические сборки, а также ДОБАВЛЯТЬ НОВЫЕ типы и члены в загруженные в оперативную память сборки.
Пространство имен System.Reflection.Emit содержит классы, позволяющие компилятору или инструментальным средствам создавать метаданные и инструкции промежуточного языка MSIL и при необходимости формировать на диске PE-файл. Эти классы предназначены в первую очередь для обработчиков сценариев и компиляторов.
Список классов, структур и перечислений, входящих в пространство, прилагается.
| Класс | Описание |
|---|---|
AssemblyBuilder |
Определяет и представляет динамическую сборку |
ConstructorBuilder |
Определяет и представляет конструктор динамического класса |
CustomAttributeBuilder |
Помогает в построении пользовательских атрибутов |
EnumBuilder |
Описывает и предоставляет тип перечисления |
EventBuilder |
Определяет события для класса |
FieldBuilder |
Определяет и предоставляет поле. Этот класс не наследуется |
ILGenerator |
Создает инструкции промежуточного языка MSIL |
LocalBuilder |
Представляет локальную переменную внутри метода или конструктора |
MethodBuilder |
Определяет и предоставляет метод (или конструктор) для динамического класса |
MethodRental |
Позволяет быстро менять реализацию основного текста сообщения метода, задающего метод класса |
ModuleBuilder |
Определяет и представляет модуль. Получает экземпляр класса ModuleBuilder с помощью вызова метода DefineDynamicModule |
|
Содержит поля, предоставляющие инструкции промежуточного языка MSIL для эмиссии членами класса ILGenerator (например методом Emit ) |
ParameterBuilder |
Создает или связывает информацию о параметрах |
PropertyBuilder |
Определяет свойства для типа |
SignatureHelper |
Обеспечивает методы построения подписей |
TypeBuilder |
Определяет и создает новые экземпляры классов во время выполнения |
UnmanagedMarshal |
Представляет класс, описывающий способ маршалирования поля из управляемого в неуправляемый код. Этот класс не наследуется |
| Структура | Описание |
|---|---|
EventToken |
Предоставляет Token, возвращаемый метаданными для представления события |
FieldToken |
Структура FieldToken является |
Label |
Представляет метку в потоке инструкций. Label используется вместе с классом ILGenerator |
MethodToken |
Структура MethodToken является |
OpCode |
Описывает инструкцию промежуточного языка MSIL |
ParameterToken |
Структура ParameterToken является закрытым представлением возвращаемой метаданными лексемы, которая используется для представления параметра |
PropertyToken |
Структура PropertyToken является закрытым представлением возвращаемого метаданными маркера Token, используемого для представления свойства |
SignatureToken |
Предоставляет Token, возвращенный метаданными для представления подписи |
StringToken |
Предоставляет лексему, которая предоставляет строку |
TypeToken |
Представляет маркер Token, который возвращается метаданными, чтобы представить тип |
| Перечисление | Описание |
|---|---|
AssemblyBuilderAccess |
Определяет режимы доступа для динамической сборки |
FlowControl |
Описывает, каким образом инструкция меняет поток команд управления |
OpCodeType |
Описывает типы инструкций промежуточного языка MSIL |
OperandType |
Описывает тип операнда инструкции промежуточного языка MSIL |
PackingSize |
Задает один из двух факторов, определяющих выравнивание занимаемой |
PEFileKinds |
Задает тип переносимого исполняемого PE-файла |
StackBehaviour |
Описывает, как значения помещаются в стек или выводятся из стека |
//––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
// Вот такой класс в составе однофайловой сборки DynamicAssm
// предполагается построить в ходе выполнения сборки
// DynamicAssemblyGenerator.
// public class DynamicTest
// {
// private string messageString;
// // Конструктор
// DynamicTest(string strKey)
// {
// messageString = strKey;
// }
//
// // Методы
// public void ShowMessageString()
// {
// System.Console.WriteLine
// ("the value of messageString is {0}...", messageString);
// }
//
// public string GetMessageString()
// {
// return messageString;
// }
//
//}
//–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
namespace DynamicAssemblyGenerator
{
// AssemblyGenerator – класс, реализующий динамическую генерацию сборки.
class AssemblyGenerator
{
public string XXX;
public string ZZZ()
{
return XXX;
}
public int CreateAssm(AppDomain currentAppDomain)
{
// Создание сборки начинается с присвоения ей имени и номера версии.
// Для этого используется класс AssemblyName.
// Определяется имя и версия создаваемой сборки.
AssemblyName assmName = new AssemblyName();
assmName.Name = "DynamicAssm";
assmName.Version = new Version("1.0.0.0");
// Создается сборка в памяти. В рамках текущего домена приложения.
// С использованием режима доступа,
// который задается одним из элементов перечисления:
// Run – динамическая сборка выполняется, но не сохраняется;
// RunAndSave – динамическая сборка выполняется и сохраняется;
// Save – динамическая сборка не выполняется, но сохраняется.
AssemblyBuilder assembly = currentAppDomain.DefineDynamicAssembly(assmName,
AssemblyBuilderAccess.Save);
// Создается однофайловая сборка, в которой имя единственного
// модуля совпадает с именем самой сборки.
ModuleBuilder module = assembly.DefineDynamicModule("DynamicAssm",
"DynamicAssm.dll" );
// Создается и определяется класс DynamicTest.
// Метод module.DefineType позволяет
// встраивать в модуль класс, структуру или интерфейс.
// Вторым параметром метода идет элемент перечисления.
// Таким образом создается объект - заготовка
// класса, который далее дополняется полями, свойствами, методами...
TypeBuilder dynamicTestClass = module.DefineType("DynamicAssm.DynamicTest",
TypeAttributes.Public);
// Объявляется данное - член класса DynamicTest.
// Предполагается объявить "private string messageString;"
FieldBuilder messageStringField =
DynamicTestClass.DefineField("messageString",
Type.GetType("System.String"),
FieldAttributes.Private);
// Объекты для генерации элементов класса.
// В данном конкретном случае используются при генерации:
ILGenerator bodyConstructorIL; // – тела конструктора.
ILGenerator methodIL; // – тела метода.
// Объявляется конструктор.______________________________________
// Предполагается объявить "DynamicTest(string strKey)..."
Type[] constructorArgs = new Type[1];
constructorArgs[0] = Type.GetType("System.String");
ConstructorBuilder constructor = dynamicTestClass.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
constructorArgs);
// Тело конструктора. Представляет собой IL-код,
// который встраивается в тело конструктора посредством метода Emit,
// определенного в классе ILGenerator
// (см. Объекты для генерации элементов класса).
// Метод Emit в качестве параметров использует перечисление OpCodes
// (коды операций), которые определяют допустимые команды IL.
bodyConstructorIL = constructor.GetILGenerator();
bodyConstructorIL.Emit(OpCodes.Ldarg_0);
Type objectClass = Type.GetType("System.Object");
ConstructorInfo greatConstructor = objectClass.GetConstructor(new Type[0]);
bodyConstructorIL.Emit(OpCodes.Call, greatConstructor);
bodyConstructorIL.Emit(OpCodes.Ldarg_0);
bodyConstructorIL.Emit(OpCodes.Ldarg_1);
bodyConstructorIL.Emit(OpCodes.Stfld,messageStringField);
bodyConstructorIL.Emit(OpCodes.Ret);
// Конец объявления конструктора._______________________________
// Объявление метода public string GetMessageString()__________
MethodBuilder GetMessageStringMethod = dynamicTestClass.DefineMethod(
"GetMessageString",
MethodAttributes.Public,
Type.GetType("System.Sring"),
null);
// IL_0000: ldarg.0
// IL_0001: ldfld string DynamicAssemblyGenerator.Assembly Generator::XXX
// IL_0006: stloc.0
// IL_0007: br.s IL_0009
// IL_0009: ldloc.0
// IL_000a: ret
//System.Reflection.Emit.Label label = new Label();
// Тело метода...
methodIL = GetMessageStringMethod.GetILGenerator();
methodIL.Emit(OpCodes.Ldarg_0);
methodIL.Emit(OpCodes.Ldfld,messageStringField);
methodIL.Emit(OpCodes.Ret);
// Конец объявления метода public string GetMessageString()__________________
// Объявление метода public string ShowMessageString()_______________________
MethodBuilder ShowMessageStringMethod = dynamicTestClass.DefineMethod(
"ShowMessageString",
MethodAttributes.Public,
null,
null);
// Тело метода...
methodIL = ShowMessageStringMethod.GetILGenerator();
methodIL.EmitWriteLine("This is ShowMessageStringMethod...");
methodIL.Emit(OpCodes.Ret);
// Конец объявления метода public string ShowMessageString()_________________
// Вот и завершили динамическое объявление класса.
dynamicTestClass.CreateType();
// Остается его сохранить на диск.
assembly.Save("DynamicAssm.dll");
return 0;
}
static void Main(string[] args)
{
// Создается и сохраняется динамическая сборка.
AssemblyGenerator ag = new AssemblyGenerator();
ag.CreateAssm(AppDomain.CurrentDomain);
// Для наглядности! создаются НОВЫЕ объекты и заново добывается
// ссылка на текущий домен приложения.
// Теперь – дело техники. Надо загрузить и выполнить сборку.
// Делали. Умеем!
AppDomain currentAppDomain = Thread.GetDomain();
AssemblyGenerator assmGenerator = new AssemblyGenerator();
assmGenerator.CreateAssm(currentAppDomain);
// Загружаем сборку.
Assembly assm = Assembly.Load("DynamicAssm");
// Объект класса Type для класса DynamicTest.
Type t = assm.GetType("DynamicAssm.DynamicTest");
// Создается объект класса DynamicTest и вызывается конструктор
// с параметрами.
object[] argsX = new object[1];
argsX[0] = "Yes, yes, yes–s–s–s!";
object obj = Activator.CreateInstance(t, argsX);
MethodInfo mi;
// "От имени" объекта - представителя класса DynamicTest
// вызывается метод ShowMessageString.
mi = t.GetMethod("ShowMessageString");
mi.Invoke(obj,null);
// "От имени" объекта - представителя класса DynamicTest
// вызывается метод GetMessageString.
// Этот метод возвращает строку, которая перехватывается
// и выводится в окне консольного приложения.
mi = t.GetMethod("GetMessageString");
//!!!//mi.Invoke(obj,null);//Этот метод не вызывается. Криво объявился? //
}
}
}
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.