2010-03-22 22 views
11

Estoy intentando crear Delegate para leer/escribir propiedades de tipo desconocido de clase en tiempo de ejecución.CreateDelegate con tipos desconocidos

que tienen una clase genérica Main<T> y un método que tiene el siguiente aspecto:

Delegate.CreateDelegate(typeof(Func<T, object>), get) 

donde get es una MethodInfo de la propiedad que debe ser leído. El problema es que cuando la propiedad devuelve int (supongo que esto sucede para los tipos de valor) el código anterior arroja ArgumentException porque el método no puede vincularse. En caso de cadena, funciona bien.

para resolver el problema He cambiado el código para que corresponda tipo de delegado se genera mediante el uso de MakeGenericType. Así que ahora el código es:

Type func = typeof(Func<,>); 
Type generic = func.MakeGenericType(typeof(T), get.ReturnType); 
var result = Delegate.CreateDelegate(generic, get) 

El problema ahora es que la instancia creada delegado de generic por lo que tiene que utilizar DynamicInvoke que sería tan lento como el uso de la reflexión pura de leer el campo.

Así que mi pregunta es por qué el primer fragmento de código falla con los tipos de valores. De acuerdo con MSDN que debería funcionar, ya que dice que

El tipo de retorno de un delegado es compatible con el tipo de retorno de un método si el tipo de retorno del método es más restrictivo que el tipo de retorno del delegado

y cómo ejecutar el delegado en el segundo fragmento para que sea más rápido que la reflexión.

Gracias.

Respuesta

10

Aquí hay una manera de resolver su problema. Crear un método genérico:

public static Func<T, object> MakeDelegate<U>(MethodInfo @get) 
{ 
    var f = (Func<T, U>)Delegate.CreateDelegate(typeof(Func<T, U>), @get); 
    return t => f(t); 
} 

De esta manera, compilador de C# 's se encarga de insertar el boxeo es necesario (si lo hay) para convertir f(t) (de tipo U) a object. Ahora puede usar el reflejo para llamar a este método MakeDelegate con U establecido en @get.ReturnType, y lo que obtenga será un Func<T, object> que se puede invocar sin necesidad de recurrir al uso de DynamicInvoke.

+0

Muchas gracias, funcionó! – Giorgi

2

Su invocación falla porque necesita un objeto que no sea un tipo de valor (como INT) - obviamente Func<T, int> no es un Func<T, Int> - no funcionará con ningún vt como double o bool. O devuelve un recuadro Int (o lo que sea que tengas). o (quizás mejor) use reflexión emitir API.

Mediante el uso de la reflexión emiten clases Puede crear métodos dinámicos y guardarlos como delegados, delegados o crear dinámicas y guardarlos en un poco de su estructura. Puede hacer esto solo una vez (quizás una vez por tiempo de ejecución) almacenarlo en algún Dict e invocar cuando sea necesario.

Espero que ayude. lucas

+0

Todos los tipos de valores heredan del objeto, por eso pensé que funcionaría. Soy consciente de que la emisión de reflejos es más complicada en comparación con CreateDelegate – Giorgi

+0

pero la covarianza y la contravariancia NO funcionan con tipos de valores. Ese es todo el propósito de los tipos de valores y el boxeo. http://msdn.microsoft.com/en-us/magazine/cc163727.aspx porque no hay una conversión IMPLICITA más aquí http://blogs.msdn.com/ericlippert/archive/2007/10/ 19/covariance-and-contravariance-in-c-part-three-member-group-conversion-variance.aspx – luckyluke

3

Su código original sólo puede trabajar para los tipos de referencia. Es por eso que la cadena no fue un problema, deriva directamente de System.Object. Que un tipo de valor se derive de ValueType y Object es una buena ilusión en el papel, pero en realidad requiere código. El compilador de C# emite automáticamente ese código, requiere una conversión de boxeo. Esa es la parte que falta aquí, no hay conversión en tiempo de ejecución de int a objeto sin el BOX opcode.

Puede obtener ese opcode en su código, pero tendrá que usar System.Reflection.Emit.

Antes de ir allí, primero compruebe si lo que tiene ahora es demasiado lento. El gasto de la reflexión es extraer los metadatos del conjunto. Eso se hizo cuando creó el delegado, la información de tipo se almacena en caché después de eso.

+1

¿Quiere decir que usar DynamicInvoke en el delegado generado en el fragmento dos es rápido o estoy equivocado? Actualmente estoy usando SetValue/GetValue para escribir/leer propiedades y quería hacerlo más rápido. – Giorgi

0

¿Es posible restringir el método genérico para que solo funcione con tipos de referencia y crear otro solo para trabajar con tipos de valores y decidir qué funcionalidad usar en consecuencia?

Cuestiones relacionadas