Российский Государственный Технологический Университет им. К.Э. Циолковского
Опубликован: 02.03.2007 | Доступ: свободный | Студентов: 5636 / 790 | Оценка: 3.96 / 3.45 | Длительность: 27:04:00
ISBN: 978-5-9556-0086-4
Лекция 10:

Атрибуты, сборки, рефлексия

Игры со сборками из 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);
 }
 }
 }
 }
Листинг 10.5.

Итак, подключили общедоступную сборку. Она не копируется, а остается в 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
OpCodes Содержит поля, предоставляющие инструкции промежуточного языка 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);//Этот метод не вызывается. Криво объявился? // 
 }
 }
 }
Листинг 10.6.
kewezok kewezok
kewezok kewezok
Елена Шляхт
Елена Шляхт
Объясните плиз в чем отличие а++ от ++а
Почему результат разный?
int a=0, b=0;
Console.WriteLine(a++); //0
Console.WriteLine(++b); //1
a++;
++b;
Console.WriteLine(a); //2
Console.WriteLine(b); //2