Как создать метод во время выполнения, используя Reflection.emit - PullRequest
9 голосов
/ 24 августа 2010

Я создаю объект во время выполнения с помощью отражения Emit.Я успешно создал поля, свойства и получил набор методов.Теперь я хочу добавить метод.Для простоты предположим, что метод просто возвращает случайное число.Как мне определить тело метода?

РЕДАКТИРОВАТЬ:

Да, я просматривал документацию по msdn вместе с другими ссылками, и я начинаю думать об этом,Я вижу, как в приведенном выше примере сложение и / или умножение, но что, если мой метод делает другие вещи.Как определить этот «материал» Предположим, что я динамически генерировал приведенный ниже класс, как бы я создал тело метода GetDetails ()?

class TestClass
{
    public string Name  { get; set; }
    public int Size  { get; set; }

    public TestClass()
    {
    }

    public TestClass(string Name, int Size)
    {
        this.Name = Name;
        this.Size = Size;
    }

    public string GetDetails()
    {
        string Details = "Name = " + this.Name + ", Size = " + this.Size.ToString();
        return Details;
    }
}

1 Ответ

20 голосов
/ 24 августа 2010

Вы используете MethodBuilder для определения методов.Чтобы определить тело метода, вы вызываете GetILGenerator () , чтобы получить ILGenerator , а затем вызываете методы Emit для выдачи отдельных инструкций IL.В документации MSDN есть пример для MethodBuilder, и вы можете найти другие примеры использования отражения на странице Использование отражения на :

public static void AddMethodDynamically(TypeBuilder myTypeBld,
                                    string mthdName,
                                    Type[] mthdParams,
                                    Type returnType,
                                    string mthdAction)
{
    MethodBuilder myMthdBld = myTypeBld.DefineMethod(
                                            mthdName,
                                            MethodAttributes.Public |
                                            MethodAttributes.Static,
                                            returnType,
                                            mthdParams);
    ILGenerator ILout = myMthdBld.GetILGenerator();
    int numParams = mthdParams.Length;
    for (byte x = 0; x < numParams; x++)
    {
        ILout.Emit(OpCodes.Ldarg_S, x);
    }
    if (numParams > 1)
    {
        for (int y = 0; y < (numParams - 1); y++)
        {
            switch (mthdAction)
            {
                case "A": ILout.Emit(OpCodes.Add);
                    break;
                case "M": ILout.Emit(OpCodes.Mul);
                    break;
                default: ILout.Emit(OpCodes.Add);
                    break;
            }
        }
    }
    ILout.Emit(OpCodes.Ret);
}

Звучиткак будто вы ищете ресурсы для написания MSIL.Одним из важных ресурсов является класс OpCodes , который имеет член для каждой инструкции IL.Документация описывает, как работает каждая инструкция.Другим важным ресурсом является либо Ildasm , либо Reflector .Это позволит вам увидеть IL для скомпилированного кода, который поможет вам понять, что IL вы хотите написать.Запуск вашего GetDetailsMethod через Reflector и установка языка на IL дает:

.method public hidebysig instance string GetDetails() cil managed
{
    .maxstack 4
    .locals init (
        [0] string Details,
        [1] string CS$1$0000,
        [2] int32 CS$0$0001)
    L_0000: nop 
    L_0001: ldstr "Name = "
    L_0006: ldarg.0 
    L_0007: call instance string ConsoleApplication1.TestClass::get_Name()
    L_000c: ldstr ", Size = "
    L_0011: ldarg.0 
    L_0012: call instance int32 ConsoleApplication1.TestClass::get_Size()
    L_0017: stloc.2 
    L_0018: ldloca.s CS$0$0001
    L_001a: call instance string [mscorlib]System.Int32::ToString()
    L_001f: call string [mscorlib]System.String::Concat(string, string, string, string)
    L_0024: stloc.0 
    L_0025: ldloc.0 
    L_0026: stloc.1 
    L_0027: br.s L_0029
    L_0029: ldloc.1 
    L_002a: ret 
}

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

ilGen.Emit(OpCodes.Nop);
ilGen.Emit(OpCodes.Ldstr, "Name = ");
ilGen.Emit(OpCodes.Ldarg_0);
ilGen.Emit(OpCodes.Call, nameProperty.GetGetMethod());
// etc..

Вы также можете поискать введение в MSIL, например: Введение в язык ассемблера IL .

...