【问题标题】:C# dynamic method: return string representation of integerC#动态方法:返回整数的字符串表示
【发布时间】:2018-10-11 10:55:56
【问题描述】:

我想创建一个动态方法,它接受Int32 参数并返回它的字符串表示:

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()));
}

但该方法失败并出现异常Object reference not set to an instance of an object。我错过了什么?

更新:我检查了我的方法的 C# 版本的 MSIL:

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

我发现我需要在调用intToString 之前从堆栈中弹出整数。完整版:

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()));

现在它可以工作了。

【问题讨论】:

标签: c# .net reflection cil dynamicmethod


【解决方案1】:

我不是 IL 专业人士,但我认为您需要将 int 装箱以在其上调用 ToString。我的猜测是 22 整数值被 JIT 视为指向对象的指针。然后,运行时将访问冲突转换为 NRE,它对小指针值执行此操作。

我建议完全放弃反射发射并使用表达式树。更简单,性能相同。

【讨论】:

  • 谢谢你的想法。我会尝试使用表达式来做同样的事情。
  • 虽然在调用 ToString 之前对 int 进行装箱是可行的,但这在时间和内存上都是低效的。正确的解决方案是,正如原始发布者最终发现的那样,并且正如您猜想的那样,ToString 的特殊版本将整数作为其接收者采用参考整数,而不是整数。请记住,在结构类型中,this 在逻辑上始终是该类型变量的引用。它不是那种类型的 value,因为 CLR 不知道 struct 类型是否可变。
  • @EricLippert 很高兴知道。这里可以看到:sharplab.io/… 获得了一个指向整型local的托管指针。
  • 是的,那个网站是纯金的!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-17
  • 2021-04-26
  • 2011-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-12
相关资源
最近更新 更多