Атрибуты, сборки, рефлексия
Игры со сборками из 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.