Bezdepbonusu7Программирование: статьи и ответы

Вопрос

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

Автор вопроса: ReLoadОтветов: 1

Привет всем

У меня есть код, который создает новую среду выполнения 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

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

cilsilverlight

Ответы

Ответ 1 · рейтинг 3 · Marc Gravell

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.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы