Árbol de expresiones usando acción ¿posible?

c# expression-trees generics reflection

Pregunta

Estoy teniendo un pequeño problema con algo en lo que estoy trabajando. Inicialmente creé una capa genérica que se encuentra entre mis objetos comerciales y la capa de acceso a datos que funciona bien. Luego, recientemente leí sobre algo llamado Expression Trees, que aparentemente es más eficiente y ha demostrado ser así, ya que cambié Activator.CreateInstance () con una expresión y mejoré exponencialmente mi capa genérica.

Todavía estoy leyendo un poco acerca de toda el área (Expresiones) pero encontré un código que quiero hacer genérico. En este momento, tienes que pasar un tipo concreto, como una cadena, int, decimal, etc. Era este bit para ser genérico. Intenté un par de cosas pero fallé. El bit que quiero genérico es Acción, no quiero pasar una cadena. Quiero poder pasar el tipo de la propiedad genéricamente, es decir, typeof (T) .GetProperty ("Forename"). PropertyType . es posible? Estaba pensando en hacer una declaración de cambio que es un poco foo bar.

Gracias de antemano, 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");
    }
}

Respuesta aceptada

En primer lugar, tenga en cuenta que esta no es una buena manera de hacerlo; no hay ninguna ventaja de rendimiento si está creando una Expression por llamada, y luego la Compile() , y luego la invoca. La reflexión sería más rápida . Si necesita rendimiento, consulte una biblioteca como "FastMember" , donde esto sería:

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

(donde se optimiza completamente a través de la meta-programación y el almacenamiento en caché automático)


Si desea que el tipo sea dinámico, hay dos opciones:

  • DynamicInvoke usando Expression.GetActionType y use DynamicInvoke - muy mal rendimiento (sugerencia: no haga esto)
  • Escríbalo como Action<object> y realice una conversión dentro de la expresión (fino)

Así que algo como:

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


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow