Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

.NET reflection emit – what am I doing wrong in writing this method in MSIL?

I am trying to dynamically create a delegate that returns a list of the values of all properties defined on an object, using reflection emit in C#.

I have started with the examples given here:
https://learn.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/exczf7b9(v=vs.100)

Then, I have used LinqPad to view the IL for the code I want to emit.
E.g., for this code:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

void Main()
{
    new Zap{ Name = "1", Value = 2 }.Test();
}


public class Zap
{
    public string Name { get; set; }
    public int Value { get; set; }
    
    public IEnumerable<object> Test()
    {
        return new List<object> {Name, Value}; // This is what I want to do...
    }
}

I got this IL code for the Test() method:

IL code

But when I run this code, I get a System.InvalidProgramException: 'Common Language Runtime detected an invalid program.' exception:

new Target().DoMagic();

public class Target
{
    delegate List<object> ListProps();
    public string MyProperty1 { get; set; } = "zap";
    public int MyProperty2 { get; set; } = 77;

    public void DoMagic()
    {
        Type[] methodArgs = { };
        DynamicMethod listPropsMethod = new DynamicMethod("ListProps", typeof(List<object>), methodArgs, typeof(Target).Module);

        ConstructorInfo? constructor = typeof(List<object>).GetConstructor(new Type[0]);
        MethodInfo? addMethod = typeof(List<object>).GetMethod("Add");

        ILGenerator il = listPropsMethod.GetILGenerator();
        il.Emit(OpCodes.Newobj, constructor);

        il.Emit(OpCodes.Dup);
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Call, this.GetType().GetProperty(nameof(MyProperty1), BindingFlags.Instance | BindingFlags.Public).GetGetMethod());
        il.Emit(OpCodes.Callvirt, addMethod);

        il.Emit(OpCodes.Dup);
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Call, this.GetType().GetProperty(nameof(MyProperty2), BindingFlags.Instance | BindingFlags.Public).GetGetMethod());
        il.Emit(OpCodes.Box, typeof(int));
        il.Emit(OpCodes.Callvirt, addMethod);

        il.Emit(OpCodes.Ret);

        var listProps = (ListProps)listPropsMethod.CreateDelegate(typeof(ListProps));
        var value = listProps();
        Console.WriteLine("Result : " + JsonSerializer.Serialize(value));
    }
}

I probably miss a lot of details (e.g., I don’t see how calling methods would know which instances to use…), but if someone can shed some light on how I can make this work, I would be grateful.

Also, will invoking the generated delegate created with emit be as fast as writing the first code snippet and compiling it? I am troubled by the reflection code I use to get the method/property information, and I wonder if this might cause some performance issues.

>Solution :

I think this should work:

delegate List<object> ListProps(Target obj);

and:

Type[] methodArgs = { typeof(Target) };
DynamicMethod listPropsMethod = new DynamicMethod("ListProps", typeof(List<object>), methodArgs, typeof(Target).Module);

ConstructorInfo? constructor = typeof(List<object>).GetConstructor(new Type[0]);
MethodInfo? addMethod = typeof(List<object>).GetMethod("Add");

ILGenerator il = listPropsMethod.GetILGenerator();
il.Emit(OpCodes.Newobj, constructor);

il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Callvirt, this.GetType().GetProperty(nameof(MyProperty1), BindingFlags.Instance | BindingFlags.Public).GetGetMethod());
il.Emit(OpCodes.Callvirt, addMethod);

il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Callvirt, this.GetType().GetProperty(nameof(MyProperty2), BindingFlags.Instance | BindingFlags.Public).GetGetMethod());
il.Emit(OpCodes.Box, typeof(int));
il.Emit(OpCodes.Callvirt, addMethod);

il.Emit(OpCodes.Ret);

var listProps = (ListProps)listPropsMethod.CreateDelegate(typeof(ListProps));
Console.WriteLine("Result : {0}", string.Join(", ", listProps(this)));

changes:

  • need to pass in a target for ldarg.0
  • need to specify a target type with box
  • need to split ldarg.0 and call / callvirt
  • always prefer callvirt on instance methods unless you really know why
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading