Атрибуты, сборки, рефлексия
Игры со сборками из 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-файл. Эти классы предназначены в первую очередь для обработчиков сценариев и компиляторов.
Список классов, структур и перечислений, входящих в пространство, прилагается.
Создание, сохранение, загрузка и выполнение сборки
//–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– // Вот такой класс в составе однофайловой сборки 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.