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(System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray).Method, new Type[] { });//и записываем данные в поле (всё ещё не целевое) l_propGetILGen.Emit (OpCodes.Stsfld, l_fieldBuilder2); // закидываем значение из стека (массив) в наше целевое статическое поле l_propGetILGen.Emit (OpCodes.Ret); Вот и всё. Ответ на вопрос «Для чего два массива?» скорее всего должен быть такой — для обеспечения потокобезопасности, значение целевого массива должно быть изменено атомарной операцией.Полный код: using System; using System.Collections.Generic; using System.Text; using System.Reflection.Emit;

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(System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray).Method, new Type[] { }); l_propGetILGen.Emit (OpCodes.Stsfld, l_fieldBuilder2); l_propGetILGen.Emit (OpCodes.Ret); System.Type l_t = null; System.Reflection.FieldInfo l_pi = (l_t = l_typeBuilder.CreateType ()).GetField («m_array»); int[] l_array = (int[])l_pi.GetValue (null); } } } l_array станет массивом с тремя элементами {1,2,3}.

© Habrahabr.ru