2015-02-13 2 views
3

Я пытаюсь создать новый тип, используя Reflection.Emit (в C#).Msil Emit статический массив в динамическом типе

код я хочу создать что-то похожее на

public class 
{ 
    public static int[] A = new int[] {1, 2, 3}; 
} 

Я первый попытался определить поле, а затем установить его значение:

var fb = tb.DefineField("A", FieldAttributes.Public | FieldAttributes.Static); 
fb.SetValue(null, new int[] {1, 2, 3}); 

, но он не работает, так как setValue поддерживается только для простых типов (int, float, ...).

Теперь я пытаюсь использовать DefineInitializedData (гораздо более длинный код, который не работает ...), но он не генерирует никакого действительного кода IL.

+4

Моим общим советом было бы кодировать то, что вы хотите достичь на C#, разобрать его с помощью ildasm, а затем посмотреть на вывод идей. –

+0

Вы определили тип A? 'DefineField (« A », FieldAttributes.Public | FieldAttributes.Static)' похоже, отсутствует параметр «Тип» или мне что-то не хватает? – nXu

+1

@nXu Это скорее всего копия и вставка. Вы действительно не можете использовать 'SetValue()', как это. – svick

ответ

4

SetValue поддерживается только для простых типов (INT, поплавка, ...)

Нет, это не так. FieldBuilder наследует SetValue() от FieldInfo, но это не имеет смысла для FieldBuilder.

Существует FieldBuilder.SetConstant(), но он работает только для полей const. И вы не можете использовать поля ссылочных типов const со значениями, отличными от null.

Что вам нужно сделать, это то же самое, что любой компилятор должен был бы сделать: создать статический конструктор, создать массив там, а затем назначить его на поле:

var fb = tb.DefineField("A", typeof(int[]), FieldAttributes.Public | FieldAttributes.Static); 

var ctor = tb.DefineTypeInitializer(); 

var il = ctor.GetILGenerator(); 

// new int[3] 
il.Emit(OpCodes.Ldc_I4_3); 
il.Emit(OpCodes.Newarr, typeof(int)); 

// array[0] = 1 
il.Emit(OpCodes.Dup); 
il.Emit(OpCodes.Ldc_I4_0); 
il.Emit(OpCodes.Ldc_I4_1); 
il.Emit(OpCodes.Stelem_I4); 

// array[1] = 2 
il.Emit(OpCodes.Dup); 
il.Emit(OpCodes.Ldc_I4_1); 
il.Emit(OpCodes.Ldc_I4_2); 
il.Emit(OpCodes.Stelem_I4); 

// arr[2] = 3 
il.Emit(OpCodes.Dup); 
il.Emit(OpCodes.Ldc_I4_2); 
il.Emit(OpCodes.Ldc_I4_3); 
il.Emit(OpCodes.Stelem_I4); 

// A = array 
il.Emit(OpCodes.Stsfld, fb); 

il.Emit(OpCodes.Ret); 

Если посмотреть на декомпилированный код, сгенерированный компилятором C#, вы можете увидеть разные ИЛ, используя что-то вроде <PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 и RuntimeHelpers.InitializeArray(). Это оптимизация, и я думаю, что если вы пишете IL вручную, будет проще использовать обычный метод, показанный выше.

+0

спасибо. Я бы предпочел иметь этот оптимизм, но я думаю, все будет хорошо с той версией, которую вы предлагаете. –

+0

несколько комментариев, чтобы полностью работать. вместо Dup, лучше загрузить поле 'cbilgen.Emit (OpCodes.Ldsfld, fb);' theField должен быть в начале. Большое спасибо, хотя! –