Albero delle espressioni che utilizza l'azione possibile?

c# expression-trees generics reflection

Domanda

Sto avendo un piccolo problema con qualcosa su cui sto lavorando. Inizialmente ho creato un livello generico tra i miei oggetti di business e il livello di accesso ai dati che funziona perfettamente. Recentemente ho letto di qualcosa chiamato Expression Trees che è apparentemente più efficiente e ha dimostrato di esserlo quando ho scambiato Activator.CreateInstance () con un'espressione e ho migliorato il mio livello generico in modo esponenziale.

Sto ancora leggendo l'intera area (Expressions) ma ho trovato un codice che voglio provare a fare generico. Al momento, devi passare un tipo concreto come una stringa, int, decimale, ecc. Sono stato un po 'generico. Ho provato un paio di cose ma non ci sono riuscito. Il bit che voglio generico è Action, non voglio passare in una stringa, voglio essere in grado di passare genericamente il tipo di proprietà, cioè typeof (T) .GetProperty ("Forename"). PropertyType . È possibile? Stavo pensando di fare una dichiarazione di commutazione che è un po 'come bar.

Grazie in anticipo, 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");
    }
}

Risposta accettata

Innanzitutto, nota che questo non è un buon modo per farlo; non vi è alcun vantaggio in termini di prestazioni se si sta costruendo Expression per chiamata, quindi si Compile() inviandolo e quindi richiamandolo. La riflessione sarebbe più veloce . Se hai bisogno di prestazioni, guarda una libreria come "FastMember" , dove questo sarebbe solo:

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

(dove è completamente ottimizzato tramite meta-programmazione e memorizzazione automatica nella cache)


Se vuoi che il tipo sia dinamico, allora ci sono due opzioni:

  • digitarlo usando Expression.GetActionType e utilizzare DynamicInvoke - prestazioni davvero scadenti (suggerimento: non farlo)
  • digitalo come Action<object> e fai un cast all'interno dell'espressione (bene)

Quindi qualcosa come:

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


Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché