2009-08-13 27 views
5
Food obj = ...; 
ILGenerator gen = (...).GetILGenerator(); 
gen.Emit(?? obj ??); // replace this 
gen.Emit(OpCodes.Call, typeof(Person).GetMethod("Eat")); 

显然不可能将obj干净地推送到评估堆栈上,但是我对可能会危害到的例如丑陋的黑客开放。可移植性。 ModuleBuilder.DefineInitializedData允许将一个System.Byte []存储在.sdata中。有任何想法吗?将对象文字提供给ILGenerator

编辑:生成的方法作为新程序集的一部分发出。

回答

1
object o = ...; 
Func<object> sneaky =() => o; 
gen.Emit(OpCodes.Call, sneaky.Method); 

在一个侧面说明,请确保您不能使用System.Linq.Expressions你的目的。下面是我在ANTLR项目之前和之后的一段代码:

之前。请注意,这里有一个错误(无法找到邮件列表的帖子),我没有找到,因为切换到“之后”将其纠正为副作用。

private static Func<object, object> BuildAccessor(MethodInfo method) 
{ 
    DynamicMethod dm = new DynamicMethod(method.DeclaringType.Name + method.Name + "MethodAccessor", typeof(object), new Type[] { typeof(object) }, method.DeclaringType); 
    var gen = dm.GetILGenerator(); 

    if (!method.IsStatic) 
    { 
     gen.Emit(System.Reflection.Emit.OpCodes.Ldarg_0); 
     gen.Emit(System.Reflection.Emit.OpCodes.Castclass, method.DeclaringType); 
    } 

    if (method.IsVirtual && !method.IsFinal) 
     gen.EmitCall(System.Reflection.Emit.OpCodes.Callvirt, method, null); 
    else 
     gen.EmitCall(System.Reflection.Emit.OpCodes.Call, method, null); 

    if (method.ReturnType.IsValueType) 
     gen.Emit(System.Reflection.Emit.OpCodes.Box, method.ReturnType); 

    gen.Emit(System.Reflection.Emit.OpCodes.Ret); 
    return (Func<object, object>)dm.CreateDelegate(typeof(Func<object, object>)); 
} 

private static Func<object, object> BuildAccessor(FieldInfo field) 
{ 
    DynamicMethod dm = new DynamicMethod(field.DeclaringType.Name + field.Name + "FieldAccessor", typeof(object), new Type[] { typeof(object) }, field.DeclaringType); 

    var gen = dm.GetILGenerator(); 
    if (field.IsStatic) 
    { 
     gen.Emit(System.Reflection.Emit.OpCodes.Ldsfld, field); 
    } 
    else 
    { 
     gen.Emit(System.Reflection.Emit.OpCodes.Ldarg_0); 
     gen.Emit(System.Reflection.Emit.OpCodes.Castclass, field.DeclaringType); 
     gen.Emit(System.Reflection.Emit.OpCodes.Ldfld, field); 
    } 

    if (field.FieldType.IsValueType) 
     gen.Emit(System.Reflection.Emit.OpCodes.Box, field.FieldType); 

    gen.Emit(System.Reflection.Emit.OpCodes.Ret); 
    return (Func<object, object>)dm.CreateDelegate(typeof(Func<object, object>)); 
} 

后:

private static Func<object, object> BuildAccessor(MethodInfo method) 
{ 
    ParameterExpression obj = Expression.Parameter(typeof(object), "obj"); 

    Expression<Func<object, object>> expr = 
     Expression.Lambda<Func<object, object>>(
      Expression.Convert(
       Expression.Call(
        Expression.Convert(obj, method.DeclaringType), 
        method), 
       typeof(object)), 
      obj); 

    return expr.Compile(); 
} 

private static Func<object, object> BuildAccessor(FieldInfo field) 
{ 
    ParameterExpression obj = Expression.Parameter(typeof(object), "obj"); 

    Expression<Func<object, object>> expr = 
     Expression.Lambda<Func<object, object>>(
      Expression.Convert(
       Expression.Field(
        Expression.Convert(obj, field.DeclaringType), 
        field), 
       typeof(object)), 
      obj); 

    return expr.Compile(); 
} 
+0

这会导致MethodAccessException,因为在新方法中不能访问本地定义的lambda。 – shivak 2009-08-13 21:58:14

+0

这很有趣,因为这正是我在我的实验性StringTemplate编译器中做的(并且它在那里工作)。 – 2009-08-13 23:00:45

+0

对不起,我的意思是从发射的新组件中无法访问。制作一个可用的参考类型似乎不合理,这就是为什么我最初觉得需要一个kludge。 – shivak 2009-08-13 23:07:14

0

我建议序列化,你所需要的对象,并发出呼叫从资源流反序列化(可能是缓存的,如果你要经常访问)。