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

Aparentemente no es posible insertar Obj limpiamente en la pila de evaluación, pero estoy abierto a hacks feos que pueden comprometer, p. portabilidad. ModuleBuilder.DefineInitializedData le permite a uno almacenar un System.Byte [] en .sdata. ¿Algunas ideas?Alimentando un objeto literal a ILGenerator

Editar: el método generado se está emitiendo como parte de un nuevo conjunto.

Respuesta

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

En una nota lateral, asegúrese de que no se puede utilizar System.Linq.Expressions para su propósito. Aquí hay una sección de mi código en el proyecto ANTLR antes y después:

Antes. Tenga en cuenta que hay un error en esto (no puedo encontrar la publicación de la lista de correo al respecto) que no tuve que buscar porque el cambio a "Después" lo corrigió como un efecto secundario.

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

Después:

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

Esto da como resultado una excepción MethodAccess porque la lambda localmente definida no es accesible en el nuevo método. – shivak

+0

Eso es interesante, porque es exactamente lo que hago en mi compilador experimental StringTemplate (y funciona allí). –

+0

Lo siento, quise decir inaccesible desde el nuevo conjunto que se emite. Hacer que un tipo de referencia esté disponible parece irrazonable y es por eso que inicialmente sentí que sería necesario un kludge. – shivak

0

Yo sugeriría serializar el objeto que necesita, y que emite una llamada a deserializar desde una secuencia de recursos (posiblemente almacena en caché, si se va a acceder a él con frecuencia).

Cuestiones relacionadas