Silverlight 3.0 C # - Использование Methodbuilder для создания метода SET и добавление MSIL с помощью ILGenerator и Emit

Привет всем

У меня есть код, который создает новую среду выполнения TYPE, он устанавливает методы GET и SET с помощью MethodBuilder. (Это пример из Интернета, и благодаря парню, который его написал, я, к сожалению, потерял ссылку на него, но он в моих мыслях)

TypeBuilder typeBuilder = module.DefineType("MyClass", TypeAttributes.Public | TypeAttributes.Class);

Таким образом я добавляю метод к классу.

    MethodAttributes GetSetAttr =
      MethodAttributes.Public |
      MethodAttributes.HideBySig;

// Define the "get" accessor method for current private field.
                MethodBuilder currGetPropMthdBldr =
                    typeBuilder.DefineMethod("get_value",
                                               GetSetAttr,
                                               typeof(string),
                                               Type.EmptyTypes);

                // Intermediate Language stuff...
                ILGenerator currGetIL = currGetPropMthdBldr.GetILGenerator();
                currGetIL.Emit(OpCodes.Ldarg_0);
                currGetIL.Emit(OpCodes.Ldfld, field);
                currGetIL.Emit(OpCodes.Ret);

                // Define the "set" accessor method for current private field.
                MethodBuilder currSetPropMthdBldr =
                    typeBuilder.DefineMethod("set_value",
                                               GetSetAttr,
                                               null,
                                               new Type[] { typeof(string) });

                // Again some Intermediate Language stuff...
                ILGenerator currSetIL = currSetPropMthdBldr.GetILGenerator();
                currSetIL.Emit(OpCodes.Ldarg_0);
                currSetIL.Emit(OpCodes.Ldarg_1);
                currSetIL.Emit(OpCodes.Stfld, field);
                currSetIL.Emit(OpCodes.Ret);



                // Last, we must map the two methods created above to our PropertyBuilder to 
                // their corresponding behaviors, "get" and "set" respectively. 
                property.SetGetMethod(currGetPropMthdBldr);
                property.SetSetMethod(currSetPropMthdBldr);

Он работает нормально, однако я хотел бы изменить метод установки на что-то более сложное, поэтому я пишу этот тестовый код.

открытый класс Клиент {частная строка _name; общедоступная строка name {get {return _name; } set {if (string.IsNullOrEmpty (value)) {throw new ValidationException ("Пожалуйста, установите значение"); } _name = значение; }} публичная строка lastname {get; установленный; }}

Скомпилируйте, а затем используйте Reflector, чтобы получить MSIL.

.method public hidebysig specialname instance void set_name(string 'value') cil managed
{
    .maxstack 2
    .locals init (
        [0] bool CS$4$0000)
    L_0000: nop 
    L_0001: ldarg.1 
    L_0002: call bool [mscorlib]System.String::IsNullOrEmpty(string)
    L_0007: ldc.i4.0 
    L_0008: ceq 
    L_000a: stloc.0 
    L_000b: ldloc.0 
    L_000c: brtrue.s L_001a
    L_000e: nop 
    L_000f: ldstr "Please set a value"
    L_0014: newobj instance void [System.ComponentModel.DataAnnotations]System.ComponentModel.DataAnnotations.ValidationException::.ctor(string)
    L_0019: throw 
    L_001a: ldarg.0 
    L_001b: ldarg.1 
    L_001c: stfld string AnnotationTest.MainPage/Customer::_name
    L_0021: ret 
}

Итак, задача состоит в том, чтобы реализовать это в коде SET EMIT.

                // Again some Intermediate Language stuff...
                ILGenerator currSetIL = currSetPropMthdBldr.GetILGenerator();
                currSetIL.Emit(OpCodes.Ldarg_0);
                currSetIL.Emit(OpCodes.Ldarg_1);
                currSetIL.Emit(OpCodes.Stfld, field);
                currSetIL.Emit(OpCodes.Ret);

Вот где я ошибаюсь, я не могу заставить его работать. Похоже, что я могу «просто» скопировать код, а мои навыки работы с MSIL ограничены. вот моя ошибка.

        currSetIL.Emit(OpCodes.Nop);  //       L_0000: nop 
        currSetIL.Emit(OpCodes.Ldarg_1);  //   L_0001: ldarg.1 
        currSetIL.Emit(OpCodes.Call bool [mscorlib]System.String::IsNullOrEmpty(string);// call bool [mscorlib]System.String::IsNullOrEmpty(string)

В строке 3 эти красные подчеркивания указывают на ошибки ...

  • Bool = ") Expetced"
  • [mscorlib] System = "; ожидается"
  • :: = ". ожидается"
  • и последний) дает "недопустимую строку термина выражения"

Интересно, почему я не могу использовать отражатель, код должен быть в порядке? или?

Решение - найти программу / метод, отображающий код MSIL, который можно использовать в операторе EMIT.

Это всего лишь пример, так что код будет изменен, поэтому это не решение для ответа на правильный код (все, что было бы неплохо, чтобы пример работал), а более постоянный «способ» получения правильного MSIL из C #.

Пью, длинный вопрос, надеюсь, у меня здесь все есть.

С уважением, ReLoad


person ReLoad    schedule 15.01.2010    source источник
comment
Повторный комментарий; AFAIK это будет довольно ручной перевод. А если серьезно, я думаю, что все это можно сделать с помощью Expression (особенно в случае новых объектов). У вас есть пример того, чего вы хотите достичь? то есть вы можете опубликовать эквивалент C # того, что вы хотите?   -  person Marc Gravell    schedule 15.01.2010


Ответы (1)


currSetIL.Emit (OpCodes.Call bool [mscorlib] System.String :: IsNullOrEmpty (string); // вызов bool [mscorlib] System.String :: IsNullOrEmpty (string)

Вам нужно получить MethodInfo - в этом случае:

currSetIL.EmitCall(OpCodes.Call,typeof(string).GetMethod("IsNullOrEmpty"),null);

На самом деле, я бы искал Expression для этого - я верю, что есть Compile для silverlight Expression, и вам не нужно изучать IL!

Обратите внимание: если есть несколько перегрузок, вам обычно нужно проделать намного больше работы, чтобы получить MethodInfo (просто так, что string.IsNullOrEmpty легко использовать). Также обратите внимание, что методы «экземпляра» должны использовать OpCodes.Callvirt; статические методы (подобные этому) должны использовать OpCodes.Call.

person Marc Gravell    schedule 15.01.2010
comment
+1 - слишком быстро для меня. Кроме того, я хочу указать, что вы, вероятно, не хотите добавлять операторы OpCodes.Nop, и это могло быть currSetIL.Emit (OpCodes.Call, typeof (string) .GetMethod (IsNullOrEmpty)) - короче, то же самое? - person Benjamin Podszun; 15.01.2010
comment
@Benjamin: Да, безусловно, nop - это общие накладные расходы отладчика, и их можно отбросить. - person Marc Gravell; 15.01.2010
comment
Хорошо, спасибо за быстрый ответ (и да, nop накладные расходы :-), но я взял его, чтобы показать форму преобразования 1: 1 MSIL в команду EMIT) Итак, вкратце, есть ли инструмент или что-то, что доставит меня от MSIL до жала EMIT. Есть много других строк в коде MSIL, которые нужно преобразовать? Или еще лучше, могу ли я добавить метод непосредственно к сгенерированному типу. Например, что-то вроде этого property.SetSetMethod (MyMethod); // Это не сработает, потому что SetMethod принимает конструктор методов, а не метод, поэтому могу ли я каким-то образом создать его на основе другого метода? Это решит проблему. - person ReLoad; 15.01.2010
comment
Привет всем. После некоторых размышлений я решил пойти другим путем. (Использование динамического) Это прозрачнее и проще. Спасибо за комментарии и ответы. С уважением, ReLoad - person ReLoad; 12.05.2010