Performance De Réflexion - Create Delegate (Propriétés C#)

j'ai des problèmes de performance avec l'utilisation de la réflexion.

J'ai donc décidé de créer délégués pour les propriétés de mes objets et jusqu'à présent obtenu ceci:

TestClass cwp = new TestClass();
var propertyInt = typeof(TestClass).GetProperties().Single(obj => obj.Name == "AnyValue");
var access = BuildGetAccessor(propertyInt.GetGetMethod());
var result = access(cwp);
static Func<object, object> BuildGetAccessor(MethodInfo method)
{
    var obj = Expression.Parameter(typeof(object), "o");

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

les résultats ont été très satisfaisants, environ 30 à 40 fois plus vite que la méthode conventionnelle ( PropertyInfo.GetValue (obj, null); )

le problème est: Comment puis-je faire un SetValue d'une propriété, qui fonctionne de la même manière? Malheureusement n'a pas obtenu un.

je le fais parce que je ne peux pas utiliser des méthodes avec <T> en raison de la structure de mon application.

22
demandé sur GSerg 2012-05-30 20:32:37

3 réponses

cela devrait fonctionner pour vous:

static Action<object, object> BuildSetAccessor(MethodInfo method)
{
    var obj = Expression.Parameter(typeof(object), "o");
    var value = Expression.Parameter(typeof(object));

    Expression<Action<object, object>> expr =
        Expression.Lambda<Action<object, object>>(
            Expression.Call(
                Expression.Convert(obj, method.DeclaringType),
                method,
                Expression.Convert(value, method.GetParameters()[0].ParameterType)),
            obj,
            value);

    return expr.Compile();
}

Utilisation:

var accessor = BuildSetAccessor(typeof(TestClass).GetProperty("MyProperty").GetSetMethod());
var instance = new TestClass();
accessor(instance, "foo");
Console.WriteLine(instance.MyProperty);

avec TestClass :

public class TestClass 
{
    public string MyProperty { get; set; }
}

imprime:

foo

16
répondu Kirk Woll 2012-05-30 17:01:35

je pense que vous seriez mieux avec CreateDelegate construire si la performance est la clé. Puisque vous connaissez la signature de la méthode à l'avance , qui ici est juste GetGetMethod et GetSetMethod du PropertyInfo , vous pouvez créer un délégué pour exécuter la méthode même avec la même signature directement. Les Expressions seraient mieux adaptées si vous aviez besoin de construire une certaine logique (pour laquelle vous n'aviez pas de méthode) pour les délégués. J'ai fait quelques analyses comparatives sur différentes routes à ce problème:

Func<S, T> Getter;
Action<S, T> Setter;
PropertyInfo Property;
public void Initialize(Expression<Func<S, T>> propertySelector)
{
    var body = propertySelector.Body as MemberExpression;
    if (body == null)
        throw new MissingMemberException("something went wrong");

    Property = body.Member as PropertyInfo;



    //approaches:

    //Getter = s => (T)Property.GetValue(s, null);

    //Getter = memberSelector.Compile();

    //ParameterExpression inst = Expression.Parameter(typeof(S));
    //Getter = Expression.Lambda<Func<S, T>>(Expression.Property(inst, Property), inst).Compile();

    //var inst = Expression.Parameter(typeof(S));
    //Getter = Expression.Lambda<Func<S, T>>(Expression.Call(inst, Property.GetGetMethod()), inst).Compile();

    //Getter = (Func<S, T>)Delegate.CreateDelegate(typeof(Func<S, T>), Property.GetGetMethod());



    //Setter = (s, t) => Property.SetValue(s, t, null);

    //var val = Expression.Parameter(typeof(T));
    //var inst = Expression.Parameter(typeof(S));
    //Setter = Expression.Lambda<Action<S, T>>(Expression.Call(inst, Property.GetSetMethod(), val),
    //                                         inst, val).Compile();

    //Setter = (Action<S, T>)Delegate.CreateDelegate(typeof(Action<S, T>), Property.GetSetMethod());
}


//Actual calls (tested under loop):
public T Get(S instance)
{
    //direct invocation:
    //return (T)Property.GetValue(instance, null);

   //calling the delegate:
   //return Getter(instance);
}
public void Set(S instance, T value)
{
    //direct invocation:
    //Property.SetValue(instance, value, null);

   //calling the delegate:
   //Setter(instance, value);
}

résultats pour environ 10000000 appels - (Get, Set):

GetValue-SetValue (direct): 3800 ms, 5500 ms

GetValue-SetValue (delegate): 3600 ms, 5300 ms

expressions compilées:

   Get: Expression.Property: 280 ms

        Expression.Call: 280 ms

        direct compile: 280 ms
   Set: 300 ms

créer délégué: 130 ms, 135 ms

appel direct de propriété: 70 ms, 70 ms

je voudrais, dans votre cas, écrire:

public static Func<S, T> BuildGetAccessor<S, T>(Expression<Func<S, T>> propertySelector)
{
    return propertySelector.GetPropertyInfo().GetGetMethod().CreateDelegate<Func<S, T>>();
}

public static Action<S, T> BuildSetAccessor<S, T>(Expression<Func<S, T>> propertySelector)
{
    return propertySelector.GetPropertyInfo().GetSetMethod().CreateDelegate<Action<S, T>>();
}

// a generic extension for CreateDelegate
public static T CreateDelegate<T>(this MethodInfo method) where T : class
{
    return Delegate.CreateDelegate(typeof(T), method) as T;
}

public static PropertyInfo GetPropertyInfo<S, T>(this Expression<Func<S, T>> propertySelector)
{
    var body = propertySelector.Body as MemberExpression;
    if (body == null)
        throw new MissingMemberException("something went wrong");

    return body.Member as PropertyInfo;
}

alors maintenant vous appelez:

TestClass cwp = new TestClass();
var access = BuildGetAccessor((TestClass t) => t.AnyValue);
var result = access(cwp);

n'est-ce pas plus simple?? Avait écrit une classe générique ici pour manipuler la chose exacte.

12
répondu nawfal 2017-05-23 11:51:37

utiliser les types dynamiques. Ils utilisent la réflexion sous le capot, mais ils sont beaucoup plus rapide.

dans le cas Contraire...

il y a des tonnes de bibliothèques libres de réflexion plus rapide là-bas avec des licences permissives. Je voudrais vous relier, mais il y en a trop, et je ne sais pas ce qui vous conviendrait. Il suffit de rechercher codeplex etc. Lorsque vous trouvez quelque chose que vous aimez, essayez-la.

Mais oui, peut-être avant ça, pense si réflexion vraiment est la réponse. Souvent il y a d'autres solutions.

Modifier: Comme Demandé...

http://geekswithblogs.net/SunnyCoder/archive/2009/06/26/c-4.0-dynamics-vs.-reflection.aspx

http://theburningmonk.com/2010/09/performance-test-dynamic-method-invocation-in-csharp-4/

http://www.mssoftwareconsulting.com/msswc/blog/post/C-40-and-dynamic-performance.aspx

c'est de notoriété publique.

2
répondu GregRos 2012-05-30 21:09:16