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.

¿Fue útil?

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.

  1. Obtenga una referencia a la MethodInfo para el método Type.GetTypeFromHandle:

    MethodInfo getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");
    
  2. 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:

  1. Empuja el RuntimeTypeHandle correspondiente a lo especificado Type referencia en la pila usando Opcodes.Ldtoken.

  2. Invocar getTypeFromHandle que saca el identificador de tipo de la pila y empuja el controlador de tipo real System.Type en la pila.

  3. 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.

  4. 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).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top