Arbre d'expression utilisant l'action possible?

c# expression-trees generics reflection

Question

J'ai un petit problème avec quelque chose sur lequel je travaille. J'ai initialement créé une couche générique située entre mes objets métier et la couche d'accès aux données, qui fonctionne correctement. J'ai récemment lu quelque chose à propos de quelque chose appelé Arbres d'expression qui est apparemment plus efficace et qui s'est avéré être vrai en échangeant Activator.CreateInstance () avec une expression et en améliorant de manière exponentielle mon calque générique.

Je suis encore en train de lire sur tout le domaine (Expressions) mais je suis tombé sur du code que je veux essayer de rendre générique. Pour le moment, vous devez passer à un type concret tel qu'une chaîne, int, décimal, etc. J'étais ce bit pour être générique. J'ai essayé plusieurs choses mais j'ai échoué. Le bit que je veux générique est Action, je ne souhaite pas transmettre une chaîne que je veux pouvoir transmettre de manière générique au type de la propriété, c'est-à-dire typeof (T) .GetProperty ("Prénom"). PropertyType . Est-ce possible? Je pensais faire une déclaration de commutateur qui est un peu foo bar.

Merci d'avance, Onam.

public class TTT<T> where T : new()
{
    public void Do(object t)
    {
        MethodInfo info = typeof(T).GetProperty("Forename").GetSetMethod();

        ParameterExpression param = Expression.Parameter(typeof(string), "val");

        MethodCallExpression call = Expression.Call(Expression.Constant(t), info,
            new ParameterExpression[] { param });

        Action<string> action = Expression.Lambda<Action<string>>(call, param).Compile();

        action("hi");
    }
}

Réponse acceptée

Tout d’abord, notez que ce n’est pas un bon moyen de le faire; il n'y a aucun avantage en termes de performances si vous créez une Expression par appel, puis Compile() -la, puis appelez-la. La réflexion serait plus rapide . Si vous avez besoin de performance, regardez une bibliothèque telle que "FastMember" , où ce serait simplement:

var accessor = TypeAccessor.Create(typeof(T));
accessor[target, "Forename"] = value;

(où cela est entièrement optimisé via la méta-programmation et la mise en cache automatique)


Si vous souhaitez que le type soit dynamique, il existe deux options:

  • tapez-le à l'aide de Expression.GetActionType et utilisez DynamicInvoke - performances vraiment mauvaises (conseil: ne le faites pas)
  • tapez-le comme Action<object> et faites un cast dans l'expression (fin)

Donc, quelque chose comme:

using System;
using System.Linq.Expressions;
using System.Reflection;
class Foo
{
    public string Forename {get;set;}
}
class Test<T>
{
    public void Do(object target, object value)
    {
        var obj = Expression.Constant(target, typeof(T));
        var member = Expression.PropertyOrField(obj, "Forename");
        var param = Expression.Parameter(typeof(object));
        Type type;
        switch(member.Member.MemberType)
        {
            case MemberTypes.Field:
                type = ((FieldInfo)member.Member).FieldType; break;
            case MemberTypes.Property:
                type = ((PropertyInfo)member.Member).PropertyType; break;
            default:
                throw new NotSupportedException(member.Member.MemberType.ToString());
        }
        var body = Expression.Assign(member, Expression.Convert(param, type));
        var lambda = Expression.Lambda<Action<object>>(body, param);
        lambda.Compile()(value);
    }
}
static class Program
{
    static void Main()
    {
        var obj = new Foo();
        new Test<Foo>().Do(obj, "abc");
        Console.WriteLine(obj.Forename);
    }
}


Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi