3

I want to create dynamic method which takes Int32 parameter and returns string representation of it:

public class Item
{
    public int Age { get; } = 22;
}

static void CreateDynamicMethod()
{
    var ageGet = typeof(Item).GetProperty("Age").GetGetMethod(true);
    var intToString = typeof(int).GetMethod("ToString", new Type[] { });

    var dm = new DynamicMethod("getAgeString", typeof(string), new[] { typeof(Item) }, typeof(Item).Module);
    var il = dm.GetILGenerator();
    il.Emit(OpCodes.Ldarg_0); //load Item instance on stack
    il.Emit(OpCodes.Callvirt, ageGet); //age 44 on stack
    il.Emit(OpCodes.Call, intToString); //call tostring for age, "44" on stack now
    il.Emit(OpCodes.Ret); //return "44"
    var agestr = (Func<Item, string>)dm.CreateDelegate(typeof(Func<Item, string>));
    Console.WriteLine(agestr.Invoke(new Item()));
}

But the method fails with exception Object reference not set to an instance of an object. What I missed?

UPDATE: I checked the MSIL of the C# version of my method:

static string GetAge(Item item)
{
    return item.Age.ToString();
}

And I found out that I need to pop integer from stack before calling intToString. Full version:

var dm = new DynamicMethod("getAgeString", typeof(string), new[] { typeof(Item) }, typeof(Item).Module);
var il = dm.GetILGenerator();
il.DeclareLocal(typeof(int)); //[NEW] declare local integer variable
il.Emit(OpCodes.Ldarg_0); //load Item instance on stack
il.Emit(OpCodes.Callvirt, ageGet); //age 44 on stack now
il.Emit(OpCodes.Stloc_0); //[NEW] pop ineteger from stack to local variable
il.Emit(OpCodes.Ldloca_S, 0); //[NEW] load address of integer variable onto stack
il.Emit(OpCodes.Call, intToString); //call tostring for age, "44" on stack now
il.Emit(OpCodes.Ret); //return "44"
var agestr = (Func<Item, string>)dm.CreateDelegate(typeof(Func<Item, string>));
Console.WriteLine(agestr.Invoke(new Item()));

and now it works.

mtkachenko
  • 5,389
  • 9
  • 38
  • 68
  • 1
    Possible duplicate of [What is a NullReferenceException, and how do I fix it?](https://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-and-how-do-i-fix-it) – croxy Oct 11 '18 at 10:57
  • 2
    @croxy I know what is NullReferenceException. I can't find the reason of it in this particular case. – mtkachenko Oct 11 '18 at 11:04

1 Answers1

1

I'm not an IL pro but I think you need to box the int to call ToString on it. My guess is that the 22 integer value is being treated like a pointer to an object by the JIT. The runtime then translates the access violation to an NRE which it does for small pointer values.

I would recommend dropping reflection emit entirely and using expression trees. Much simpler, same performance.

usr
  • 168,620
  • 35
  • 240
  • 369
  • Thank you for idea. I'll try to do the same thing using expressions. – mtkachenko Oct 11 '18 at 11:33
  • 1
    Though boxing the int before calling ToString would work, that's inefficient in both time and memory. The correct solution is, as the original poster eventually figured out and as you conjecture, that the special version of `ToString` that takes an integer as its receiver **takes a ref integer**, not an integer. Remember, **in a struct type, `this` is always logically a ref to a variable of that type**. It's not a *value* of that type, because the CLR does not know whether the struct type is mutable or not. – Eric Lippert Oct 11 '18 at 11:55
  • 1
    @EricLippert that is good to know. It can be seen here: https://sharplab.io/#v2:C4LglgNgPgAgDAAhgRgNwFgBQMDMSBMCAwggN5YKVJ4wAsCAsgBQCUZFVnYAdsAgB4IAvAmT4ctDJk6d+AOgAqAewDKwAE48A5qymcAvln1A A managed pointer to the integer local is obtained. – usr Oct 11 '18 at 12:18
  • Yep, that web site is pure gold! – Eric Lippert Oct 11 '18 at 12:21