2011-11-08 13 views
9

Tengo un método que acepta una instancia Expression<Func<T, object>>. Deseo obtener el tipo de datos real devuelto por una instancia de expresión específica, en lugar de object.Obtenga el tipo de devolución real de una instancia de expresión <Func <T, object>>

Lo puedo hacer funcionar para las referencias de propiedades directas, por lo que si paso la expresión x => x.IntegerProperty puedo obtener una referencia de tipo para un entero. Este enfoque requiere convertirlo a MemberExpression.

Sin embargo, no puedo hacer que funcione para expresiones arbitrarias. Por ejemplo, si la expresión es x => x.IntegerProperty.ToString(), quiero obtener una referencia de tipo para una cadena. No puedo compilar esto en MemberExpression, y si solo lo .Compile() y verifico el tipo de devolución, obtengo "object".

¿Cómo puedo ver la instancia de expresión específica y derivar el tipo de devolución real?

+0

Técnicamente, el tipo de retorno real de la expresión es ... 'object'. Como se requería que la función devolviera 'object', se generaron las expresiones necesarias para garantizar que sea el tipo devuelto (una conversión en este caso). –

Respuesta

17

Algo como esto podría hacer el truco. Probablemente no cubra todas las posibilidades, pero es un comienzo.

public static Type GetObjectType<T>(Expression<Func<T, object>> expr) 
{ 
    if ((expr.Body.NodeType == ExpressionType.Convert) || 
     (expr.Body.NodeType == ExpressionType.ConvertChecked)) 
    { 
     var unary = expr.Body as UnaryExpression; 
     if (unary != null) 
      return unary.Operand.Type; 
    } 
    return expr.Body.Type; 
} 
+0

No pude pensar en ningún otro tipo de expresión que esto podría ser si no fuera realmente 'objeto'. Cubre todos los casos que pude pensar. –

+0

Funcionó muy bien. Estoy usando esto en un conjunto bastante específico de circunstancias, así que si hay algunos casos extremos que no has cubierto, todavía no me están causando ningún dolor. ¡Gracias! –

+0

¿No es expr.Body.Type suficiente? Obtengo un resultado incorrecto cuando uso el código anterior con, por ejemplo, i => (float) i donde i es un entero. Entonces este código devuelve un número entero y no flota. Pero simplemente expr.Body.Type funciona. –

2

Aunque no es imposible, esto es particularmente difícil. Exigiría caminar por el árbol de expresiones y hacer una lógica potencialmente compleja. Por ejemplo, ¿qué te gustaría ver si pasara en la siguiente expresión?

Func<bool, object> expr = switch => switch ? 1 : "False"; 

Este método podría o bien devuelve un int o una string.

Ahora, es posible que pueda hacer más progresos al descargar parte de esta lógica en el compilador. Puede cambiar su parámetro de método de Func<T, object> a Func<T, TReturn> y usar typeof(TReturn) dentro del método para determinar qué compilador decidió el tipo de devolución de la expresión.

Por supuesto, en el caso de mi ejemplo, seguirá trabajando contra object. Sin embargo, su ejemplo de x => x.IntegerProperty.ToString() arrojará string, que es lo que está buscando.

+0

Hace algunos buenos puntos, pero esto se está utilizando en algunas circunstancias específicas donde esos bordes no deberían aparecer (o pueden evitarse fácilmente al conocerlos). ¡Gracias! –

+2

En realidad, @Adam Maras, su ejemplo nunca compilaría porque el operador ternario arrojaría un error de tiempo de compilación diciendo "El tipo de expresión condicional no se puede determinar porque no hay una conversión implícita entre 'int' y 'cadena'" –

+0

fue más un ejercicio de pensamiento que una muestra de código literal. –

0

poco de una manera descarada (y que en realidad implica la invocación de la Func), pero se puede hacer esto:

using System; 

class Program 
{ 
    static Func<T,object> MakeFunc<T>() 
    { 
     return x => 23; 
    } 

    static Type GetReturnType<T>(Func<T,object> f) 
    { 
     return f(default(T)).GetType(); 
    } 

    static void Main(string[] args) 
    { 
     Type t = GetReturnType(MakeFunc<string>()); 
     Console.WriteLine(t); 
    } 
} 

No está garantizado para trabajar en todas las situaciones, debo añadir - sobre todo si el default(T) ISN no es un parámetro válido para el Func. Pero es un posible punto de partida, al menos.

+2

No estoy seguro de cómo me siento al ejecutar la expresión para obtener el tipo de devolución; ¿Y si hay efectos secundarios para la función?En mi caso específico, eso no es una gran preocupación, pero acepté una respuesta que parece funcionar igual de bien y no requiere ejecución. ¡Gracias! –

+0

@Seth: Personalmente, no estaría muy contento con la ejecución de la expresión (es una limitación del enfoque) - fue lo mejor que pude sacar de mi cabeza. La única ventaja de este enfoque es que funciona en instancias 'Func' en lugar de instancias' Expression'. –

Cuestiones relacionadas