2009-08-13 29 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

我建議序列化,你所需要的對象,併發出呼叫從資源流反序列化(可能是緩存的,如果你要經常訪問)。