Reflection.Emit: инициализация массива
Логичным дополнением к прошлой статье был-бы наглядный пример, хотя-бы для того, чтобы показать что не так страшен чёрт, как его рисуют, и на самом деле даже если вам необходимо собрать инициализатор массива через Reflection.Emit, то большинство лишних телодвижений возьмёт на себя API, а от вас остаётся, по большей части, только слизать придуманный компилятором код из прошлой статьи. В этом примере я ограничусь простым статическим массивом на 3 System.Int32 элемента.Ну, а начнём мы с того, с чего начинается едва ли не каждый пост про Reflection.Emit — создание динамической сборки, модуля и типа:
TypeBuilder l_typeBuilder = AppDomain.CurrentDomain.
DefineDynamicAssembly
(
new System.Reflection.AssemblyName («Reflection.Emit array initilization example»),
System.Reflection.Emit.AssemblyBuilderAccess.RunAndSave
).//новая динамическая сборка
DefineDynamicModule («Example»).//обьявление модуля
DefineType («ExampleClass»);//обьявление типа
Вот и готов TypeBuilder. Одним из странных моментов прошлой статьи были вложенный тип с директивой .size и соответствующее этому типу поле. Вам не придётся об этом заботиться, ибо над этим уже подумали в Microsoft: метод TypeBuilder.DefineInitializedData придумает и класс, и поле, запишет данные куда надо, и придумает всю мишуру, которая только может им пригодиться. Это, кстати, следующий шаг:
FieldBuilder l_fieldBuilder = l_typeBuilder.DefineInitializedData («initialization_data», new byte[]{ 0×01, 0×00, 0×00, 0×00, 0×02, 0×00, 0×00, 0×00, 0×03, 0×00, 0×00, 0×00 }, System.Reflection.FieldAttributes.Static);
Я не стал заморачиваться с названием поля и обозвал его так, как обозвал. Как и должно быть, данными этого массива являются три числа (1,2,3) в little endian. Т.к. целевой массив статический, то и атрибут стоит соответствующий (Стоит заметить что это всё ещё не наш целевой массив). Обьявление целевого поля будет, думаю, многим уже знакомо:
FieldBuilder l_fieldBuilder2 = l_typeBuilder.DefineField («m_array», typeof (int[]), System.Reflection.FieldAttributes.Static | System.Reflection.FieldAttributes.Public);
Поле как поле, ничего не обычного: статическое и публичное. Но как и любое послушное (а может, и непослушное тоже) статическое поле, оно должно инициализироваться в статическом конструкторе. Посему, пришло время создавать тот самый статический конструктор. На этот момент у MS тоже заготовлена загогулина:
ConstructorBuilder l_cb = l_typeBuilder.DefineTypeInitializer ();
После этого остаётся только создать ILGenerator и написать тело метода. Код аналогичен коду из прошлой статьи, за исключенем того, что используются опкоды для статических полей:
ILGenerator l_propGetILGen = l_cb.GetILGenerator ();
l_propGetILGen.Emit (OpCodes.Ldc_I4, 3);//отправляем размер массива в стек
l_propGetILGen.Emit (OpCodes.Newarr, typeof (int));//и создаём массив, размером в 3 элемента
l_propGetILGen.Emit (OpCodes.Dup);//дублируем ссылку. InitializeArray возвращает void, так что если этого не сделать, то ссылка на созданный массив просто пропадёт
l_propGetILGen.Emit (OpCodes.Ldtoken, l_fieldBuilder);//закидываем в стек токен инициализируемого поля. Поле хранит в себе RVA своего сегмента данных
l_propGetILGen.EmitCall (OpCodes.Call, new System.Action
namespace ReflectionEmitArrayInitializerExample
{
class Program
{
static void Main (string[] args)
{
TypeBuilder l_typeBuilder = AppDomain.CurrentDomain.
DefineDynamicAssembly
(
new System.Reflection.AssemblyName («Reflection.Emit array initilization example»),
System.Reflection.Emit.AssemblyBuilderAccess.RunAndSave
).
DefineDynamicModule («Example»).
DefineType («ExampleClass»);
FieldBuilder l_fieldBuilder = l_typeBuilder.DefineInitializedData («initialization_data», new byte[]{ 0×01, 0×00, 0×00, 0×00, 0×02, 0×00, 0×00, 0×00, 0×03, 0×00, 0×00, 0×00 }, System.Reflection.FieldAttributes.Static);
FieldBuilder l_fieldBuilder2 = l_typeBuilder.DefineField («m_array», typeof (int[]), System.Reflection.FieldAttributes.Static | System.Reflection.FieldAttributes.Public);
ConstructorBuilder l_cb = l_typeBuilder.DefineTypeInitializer ();
ILGenerator l_propGetILGen = l_cb.GetILGenerator ();
l_propGetILGen.Emit (OpCodes.Ldc_I4, 3);
l_propGetILGen.Emit (OpCodes.Newarr, typeof (int));
l_propGetILGen.Emit (OpCodes.Dup);
l_propGetILGen.Emit (OpCodes.Ldtoken, l_fieldBuilder);
l_propGetILGen.EmitCall (OpCodes.Call, new System.Action