Search code examples
c#.netcilreflection.emitpropertyinfo

ILGenerator Emit : Load propertyInfo has method parameter


I'm trying to make this code in IL using ILGenerator.Emit

class Foo
{
    ...
}

public class TestClass
{       
    public static void test()
    {
        Type t = typeof(Foo);

        foreach(PropertyInfo p in t.GetProperties())
        {
            GenerateVar(p.PropertyInfo);
        }
    }

    public static object GenerateVar(Type var)
    {
        if (var == typeof(Int32))
        {
            return 5;
        }
        else if (var == typeof(Char))
        {
            return 'a';
        }

        return null;
    }
}

This is what I've done so far, and had some complications:

    MethodInfo mi = TestClass.GetType().GetMethod("GenerateVar", 
                                             BindingFlags.Public | 
                                             BindingFlags.Instance);

    ILGenerator generator = mb.GetILGenerator();

    LocalBuilder propertyType;
    LocalBuilder TestClass = mb_gen.DeclareLocal(typeof(TestClass));

    foreach (PropertyInfo pi in t.GetProperties())
    {
        propertyType = mb_gen.DeclareLocal(pi.PropertyType);

        //loads into the stack the current PropertyType and the method class
        generator.Emit(OpCodes.Ldloc, TestClass);
        generator.Emit(OpCodes.LdLoc, propertyType);


        //calls GenerateVar(Type var) to get a PropertyType var
        generator.Emit(OpCodes.Callvirt, mi);
    }

It gives me the following exception: -> expected type: System.Type , Received type: System.String

System.String is the property type that was given by: pi.PropertyType;

What am I doing wrong?

Thanks in advance


Solution

  • As thehennyy comment, if you can give us the full code we will can to help better. I'm trying to help here because I guess what you try to do.

    So I based here on your C# code. As I understand you want to create a method that get properties of type (Foo in your case) and foreach of them, get some value based on type.

    Here is a snippet of doing it for first property of the type. To complete the code you need to emit the loop, or, like you wrote in your question, loop on the properties in your C# code and emit the code for each property one after one.

    static void CallGenerateVar()
    {
        var dm = new DynamicMethod("CallGenerateVar", typeof(object), Type.EmptyTypes, typeof(TestClass));
        MethodInfo generateVarMethod = typeof(TestClass).GetMethod("GenerateVar", BindingFlags.Public | BindingFlags.Instance);
        var ilGen = dm.GetILGenerator();
        var properties = ilGen.DeclareLocal(typeof(PropertyInfo[]));
        var index = ilGen.DeclareLocal(typeof(int));
        var propInfo = ilGen.DeclareLocal(typeof(PropertyInfo));
        ilGen.Emit(System.Reflection.Emit.OpCodes.Ldtoken, typeof(Foo));
        ilGen.Emit(System.Reflection.Emit.OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", BindingFlags.Static | BindingFlags.Public));
        ilGen.Emit(System.Reflection.Emit.OpCodes.Callvirt, typeof(Type).GetMethod("GetProperties", Type.EmptyTypes));
        ilGen.Emit(System.Reflection.Emit.OpCodes.Stloc_0);
        ilGen.Emit(System.Reflection.Emit.OpCodes.Ldc_I4_0);
        ilGen.Emit(System.Reflection.Emit.OpCodes.Stloc_1);
        ilGen.Emit(System.Reflection.Emit.OpCodes.Ldloc_0);
        ilGen.Emit(System.Reflection.Emit.OpCodes.Ldloc_1);
        ilGen.Emit(System.Reflection.Emit.OpCodes.Ldelem_Ref);
        ilGen.Emit(System.Reflection.Emit.OpCodes.Stloc_2);
        ilGen.Emit(System.Reflection.Emit.OpCodes.Ldloc_0);
        ilGen.Emit(System.Reflection.Emit.OpCodes.Ldloc_2);
        ilGen.Emit(System.Reflection.Emit.OpCodes.Callvirt, typeof(PropertyInfo).GetMethod("get_PropertyType", BindingFlags.Instance | BindingFlags.Public));
        ilGen.Emit(System.Reflection.Emit.OpCodes.Callvirt, generateVarMethod);
        ilGen.Emit(System.Reflection.Emit.OpCodes.Ret);
    
        var del = (Func<object>)dm.CreateDelegate(typeof(Func<object>));
        var result = del.Invoke();
    }
    

    In case our Foo type looks like this:

    class Foo
    {
        public int MyProperty { get; set; }
    }
    

    And GenerateVar looks like this:

    public object GenerateVar(Type var)
    {
        if (var == typeof(Int32))
        {
            return 5;
        }
        else if (var == typeof(Char))
        {
            return 'a';
        }
        return null;
    }
    

    It will print 5