2010-01-30 6 views
15

¿Hay una manera más rápida para echar a Fun<TEntity, TId>Func<TEntity, object>¿Forma más rápida de emitir un Func <T, T2> a Func <T, object>?

public static class StaticAccessors<TEntity> 
{ 
public static Func<TEntity, TId> TypedGetPropertyFn<TId>(PropertyInfo pi) 
{ 
    var mi = pi.GetGetMethod(); 
    return (Func<TEntity, TId>)Delegate.CreateDelegate(typeof(Func<TEntity, TId>), mi); 
} 

public static Func<TEntity, object> ValueUnTypedGetPropertyTypeFn(PropertyInfo pi) 
{ 
    var mi = typeof(StaticAccessors<TEntity>).GetMethod("TypedGetPropertyFn"); 
    var genericMi = mi.MakeGenericMethod(pi.PropertyType); 
    var typedGetPropertyFn = (Delegate)genericMi.Invoke(null, new[] { pi }); 

    //slow: lambda includes a reflection call 
    return x => typedGetPropertyFn.Method.Invoke(x, new object[] { }); //can we replace this? 
} 
} 

¿Hay una manera de convertir a un typedGetPropertyFnFunc<TEntity, object> sin tener reflejo en el código devuelto lambda como el ejemplo anterior?

EDIT: solución modificada añadido

Ok gracias a 280Z28 para que me lleva por el camino correcto, que he incluido en la solución final a continuación. Dejé el código de reflexión para las plataformas que no son compatibles con Expressions. Para las plataformas que lo hacen muestra un 26x a 27x (13/.5 tics avg) aumento de rendimiento para obtener int y string propiedades.

public static Func<TEntity, object> ValueUnTypedGetPropertyTypeFn(PropertyInfo pi) 
{ 
    var mi = typeof(StaticAccessors<TEntity>).GetMethod("TypedGetPropertyFn"); 
    var genericMi = mi.MakeGenericMethod(pi.PropertyType); 
    var typedGetPropertyFn = (Delegate)genericMi.Invoke(null, new[] { pi }); 

    #if NO_EXPRESSIONS 
    return x => typedGetPropertyFn.Method.Invoke(x, new object[] { }); 
    #else 
    var typedMi = typedGetPropertyFn.Method; 
    var obj = Expression.Parameter(typeof(object), "oFunc"); 
    var expr = Expression.Lambda<Func<TEntity, object>> (
      Expression.Convert(
       Expression.Call(
        Expression.Convert(obj, typedMi.DeclaringType), 
        typedMi 
       ), 
       typeof(object) 
      ), 
      obj 
     ); 
    return expr.Compile(); 
    #endif 
} 

Respuesta

6

Como saben, se puede obtener una MethodInfo de PropertyInfo.GetGetMethod(). A partir de ahí, puede usar lo siguiente para obtener un Func<object, object> para recuperar esa propiedad. Por un método similar, puede devolver un Func<TObject, TResult> fuertemente tipado. Para cualquier MethodInfo dado, debe almacenar en caché los resultados de esta llamada si la necesita más de una vez, ya que este método es al menos un orden de magnitud más caro que llamar al delegado resultante.

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(); 
} 
5

En .NET 4.0 puede hacer esto porque el delegado de Func marca TResult con el modificador de salida. .NET 3.5 no es compatible con generic covariance/contravariance por lo que no se puede simplemente lanzar. No estoy seguro de si hay otra forma inteligente de hacerlo que sea más rápida que la reflexión.

Aquí está the .NET 4.0 doc page for Func. Tenga en cuenta que TResult está marcado con "out", por lo que su valor de retorno se puede convertir como un tipo menos específico como objeto.


Para un ejemplo rápido que no tiene dependencias externas, el siguiente código no se compila en .NET 3.5 pero se compila y se ejecuta correctamente en .NET 4.0.

// copy and paste into LINQpad 
void Main() 
{ 
    Func<int, string> func1 = GetString; 
    string res1 = func1(1); 
    res1.Dump(); 

    Func<int, object> func2 = func1; 
    object res2 = func2(1); 
    res2.Dump(); 
} 

public string GetString<T>(T obj) { 
    return obj.ToString(); 
} 
+0

¿Qué sucede si mi función inicial devuelve un tipo de valor como Guid? Luego recibo un error de tiempo de ejecución cuando intento enviar contenido a Func . –

5

¿ha considerado la siguiente manera:

Func<Foo, Bar> typed = (f) => return new Bar(); 
Func<Foo, object> untyped = (f) => typed(f); 

De esta manera usted acaba de envolver el delegado.

+0

No puedo hacer esto porque no tengo el tipo de compilación de 'Bar' – mythz

+1

Si tiene el tipo' Bar' en tiempo de compilación, esta será la forma más fácil y "olímpica" de hacer esto XD . –

Cuestiones relacionadas