问题
Food obj = ...;
ILGenerator gen = (...).GetILGenerator();
gen.Emit( ?? obj ?? ); // replace this
gen.Emit(OpCodes.Call, typeof(Person).GetMethod("Eat"));
It's apparently not possible to cleanly push obj onto the evaluation stack, but I am open to ugly hacks which might compromise e.g. portability. ModuleBuilder.DefineInitializedData allows one to store a System.Byte[] in the .sdata. Any ideas?
Edit: the generated method is being emitted as part of a new assembly.
回答1:
object o = ...;
Func<object> sneaky = () => o;
gen.Emit(OpCodes.Call, sneaky.Method);
On a side note, make sure you can't use System.Linq.Expressions for your purpose. Here's a section of my code in the ANTLR project before and after:
Before. Note that there's a bug in this (can't find the mailing list post about it) that I didn't have to find because the switch to "After" corrected it as a side effect.
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>));
}
After:
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();
}
回答2:
I would suggest serializing the object you need, and emitting a call to deserialize it from a resource stream (possibly cached, if you are going to access it frequently).
来源:https://stackoverflow.com/questions/1269967/feeding-an-object-literal-to-ilgenerator