2010-01-30 17 views
15

Y at-il un moyen plus rapide de lancer Fun<TEntity, TId> à Func<TEntity, object>Une manière plus rapide de lancer un Func <T, T2> à 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? 
} 
} 

est-il un moyen de convertir typedGetPropertyFn à un Func<TEntity, object> sans avoir le code de réflexion dans le lambda retourné comme dans l'exemple ci-dessus?

EDIT: solution modifiée ajouté

Ok merci à 280Z28 pour me conduire sur le droit chemin que j'ai inclus dans la solution finale ci-dessous. J'ai laissé le code de réflexion là pour les plateformes qui ne supportent pas les expressions. Pour les plates-formes qui le font, il montre un 26x à 27x (13/0,5 ticks moy) augmentation perf pour obtenir int et string propriétés.

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 
} 

Répondre

6

Comme vous le savez, vous pouvez obtenir un MethodInfo de PropertyInfo.GetGetMethod(). À partir de cela, vous pouvez utiliser ce qui suit pour obtenir un Func<object, object> pour récupérer cette propriété. Par une méthode similaire, vous pouvez renvoyer un Func<TObject, TResult> fortement typé. Pour tout MethodInfo donné, vous devriez mettre en cache les résultats de cet appel si vous en avez besoin plus d'une fois puisque cette méthode est au moins d'un ordre de grandeur plus coûteuse que d'appeler le délégué résultant.

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

Dans .NET 4.0, vous pouvez le faire car le délégué Func marque TResult avec le modificateur out. .NET 3.5 ne prend pas en charge generic covariance/contravariance donc vous ne pouvez pas simplement lancer. Je ne suis pas sûr s'il y a une autre façon intelligente de le faire qui est plus rapide que la réflexion.

Voici the .NET 4.0 doc page for Func. Notez que TResult est marqué avec "out" afin que sa valeur de retour puisse être convertie en un type moins spécifique tel qu'un objet. Pour un exemple rapide qui n'a pas de dépendances externes, le code suivant ne parvient pas à compiler sur .NET 3.5 mais compile et s'exécute correctement sur .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

Que faire si ma fonction initiale renvoie un type de valeur tel que Guid? Ensuite, j'obtiens une erreur d'exécution lorsque je tente de lancer vers Func . –

5

Avez-vous envisagé de faire ce qui suit:

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

De cette façon, vous enveloppez tout le délégué.

+0

Je ne peux pas faire cela parce que je n'ai pas le type de 'Bar' au moment de la compilation – mythz

+1

Si vous aviez le type' Bar' au moment de la compilation, ce sera le moyen le plus facile et "olympique" de faire ce XD . –