C# Llama y devuelve un objeto desde un método estático en IL
-
09-12-2019 - |
Pregunta
Esta es una extensión de las soluciones ofrecidas. aquí.He creado un método estático que me devuelve un objeto.Mi objetivo es escribir un método dinámico para un tipo que defino en tiempo de ejecución para devolverme el objeto que devuelve este método estático.Mi código hasta ahora:
// type builder and other prep stuff removed for sake of space and reading
private void EmitReferenceMethodBody(Type returnType)
{
MethodBuilder builder =
typeBuilder.DefineMethod(
method.Name,
MethodAttributes.Virtual | MethodAttributes.Public,
method.CallingConvention,
method.ReturnType,
typeArray1);
builder.InitLocals = true;
ILGenerator gen = builder.GetILGenerator();
MethodInfo getStoredObject = typeof(ObjectStore).GetMethod("GetStoredObject", BindingFlags.Public | BindingFlags.Static);
MethodInfo getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");
gen.Emit(OpCodes.Ldtoken, returnType);
gen.Emit(OpCodes.Call, getTypeFromHandle);
gen.Emit(OpCodes.Call, getStoredObject);
gen.Emit(OpCodes.Ret);
}
El código actualizado ahora llama al método pero parece estar pasando el tipo creado dinámicamente en lugar de la variable returnType.
Solución
Al menos un problema es que estás presionando la referencia "esta" (OpCodes.Ldarg_0
) en la pila aunque nunca se extrae (ya que estás invocando un estático método).Intentaría eliminar esa línea y ver si se comporta mejor.
Otro problema es que estás de paso. new Type[] { returnType }
hacia EmitCall
método.Eso está destinado a argumentos opcionales (params
) y sospecho que su método en realidad no tiene ningún parámetro.Por lo tanto, también deberías eliminar ese argumento.
Editar:
Según los comentarios, está intentando pasar un System.Type
objeto conocido estáticamente por un método que está invocando dinámicamente.Esto es posible, pero es necesario superar un par de obstáculos.
Obtenga una referencia a la
MethodInfo
para el métodoType.GetTypeFromHandle
:MethodInfo getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");
Utilice las siguientes líneas de IL para impulsar su
returnType
en la pila:gen.Emit(OpCodes.Ldtoken, returnType); gen.Emit(OpCodes.Call, getTypeFromHandle);
En resumen, su código debería verse así:
MethodInfo getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");
gen.Emit(OpCodes.Ldtoken, returnType);
gen.Emit(OpCodes.Call, getTypeFromHandle);
gen.EmitCall(OpCodes.Call, getStoredObject);
gen.Emit(OpCodes.Ret);
El comportamiento de la pila de transición de este código es:
Empuja el
RuntimeTypeHandle
correspondiente a lo especificadoType
referencia en la pila usandoOpcodes.Ldtoken
.Invocar
getTypeFromHandle
que saca el identificador de tipo de la pila y empuja el controlador de tipo realSystem.Type
en la pila.Llame a su método estático, que mostrará el
Type
argumento fuera de la pila y envíe el valor de retorno de su propio método a la pila.Regreso del método.
Otros consejos
Los árboles de expresión podrían ser una mejor solución en este caso.Es bastante fácil crear un Func<Type, object>
usando escritura dinámica a través de expresiones.
ParameterExpression paramEx = Expression.Parameter(typeof(Type), "paramObject");
Expression callExpression = Expression.Call(typeof(ObjectStore), "GetStoredObject", null, paramEx);
Expression<Func<Type, object>> funcExpression = Expression.Lambda<Func<Type, object>>(callExpression, paramEx);
Func<Type, object> actualFunc = funcExpression.Compile();
Ahora, si ObjectStore requiere parámetros genéricos, reemplazaría typeof(ObjectStore)
con typeof(ObjectStore).MakeGenericType(returnType)
.Si el GetStoredObject
La función en sí requiere parámetros genéricos, entonces cambiarías el null
en el Expression.Call
declaración a new Type[] { returnType }
.Si esto se genera una vez para cada tipo que ingresa y planea usarlo con frecuencia, entonces podría ser una buena idea almacenar en caché estas funciones en un Dictionary<Type, Func<Type, object>>
y compílelo solo una vez (ya que compilar expresiones repetidamente es un desperdicio de recursos del sistema).