wie man Ausdrucksbaum / Lambda für eine tiefe Eigenschaft von einer Zeichenkette anlegt

c# expression-trees lambda

Frage

Gegeben eine Zeichenfolge: "Person.Address.Postcode" Ich möchte in der Lage sein, diese Postleitzahleigenschaft für eine Instanz von Person abzurufen / festzulegen. Wie kann ich das machen? Meine Idee war, die Zeichenfolge durch "." und dann iteriere über die Teile, suche nach der Eigenschaft des vorherigen Typs und baue dann einen Ausdrucksbaum auf, der ungefähr so ​​aussieht (Entschuldigung für die Pseudosyntax):

(person => person.Address) address => address.Postcode

Ich habe wirklich Schwierigkeiten, den Ausdrucksbaum tatsächlich zu erstellen! Wenn dies der beste Weg ist, kann jemand vorschlagen, wie es geht, oder gibt es eine einfachere Alternative?

Vielen Dank

Andrew

(person => person.Address) address => address.Postcode

Akzeptierte Antwort

Warum verwenden Sie keine Rekursion? Etwas wie:

setProperyValue(obj, propertyName, value)
{
  head, tail = propertyName.SplitByDotToHeadAndTail(); // Person.Address.Postcode => {head=Person, tail=Address.Postcode}
  if(tail.Length == 0)
    setPropertyValueUsingReflection(obj, head, value);
  else
    setPropertyValue(getPropertyValueUsingReflection(obj, head), tail, value); // recursion
}

Expertenantwort

Es hört sich so an, als ob Sie mit normaler Reflektion sortiert wurden, aber für Informationen wäre der Code zum Erstellen eines Ausdrucks für verschachtelte Eigenschaften diesem Order-by-Code sehr ähnlich.

Beachten Sie, dass Sie zum GetSetMethod() eines Werts GetSetMethod() für die Eigenschaft verwenden müssen, und rufen Sie dies auf. Es gibt keinen integrierten Ausdruck zum Zuweisen von Werten nach der Konstruktion (obwohl dies in 4.0 unterstützt wird ).

(bearbeiten) so:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
class Foo
{
    public Foo() { Bar = new Bar(); }
    public Bar Bar { get; private set; }
}
class Bar
{
    public string Name {get;set;}
}
static class Program
{
    static void Main()
    {
        Foo foo = new Foo();
        var setValue = BuildSet<Foo, string>("Bar.Name");
        var getValue = BuildGet<Foo, string>("Bar.Name");
        setValue(foo, "abc");
        Console.WriteLine(getValue(foo));        
    }
    static Action<T, TValue> BuildSet<T, TValue>(string property)
    {
        string[] props = property.Split('.');
        Type type = typeof(T);
        ParameterExpression arg = Expression.Parameter(type, "x");
        ParameterExpression valArg = Expression.Parameter(typeof(TValue), "val");
        Expression expr = arg;
        foreach (string prop in props.Take(props.Length - 1))
        {
            // use reflection (not ComponentModel) to mirror LINQ 
            PropertyInfo pi = type.GetProperty(prop);
            expr = Expression.Property(expr, pi);
            type = pi.PropertyType;
        }
        // final property set...
        PropertyInfo finalProp = type.GetProperty(props.Last());
        MethodInfo setter = finalProp.GetSetMethod();
        expr = Expression.Call(expr, setter, valArg);
        return Expression.Lambda<Action<T, TValue>>(expr, arg, valArg).Compile();        

    }
    static Func<T,TValue> BuildGet<T, TValue>(string property)
    {
        string[] props = property.Split('.');
        Type type = typeof(T);
        ParameterExpression arg = Expression.Parameter(type, "x");
        Expression expr = arg;
        foreach (string prop in props)
        {
            // use reflection (not ComponentModel) to mirror LINQ 
            PropertyInfo pi = type.GetProperty(prop);
            expr = Expression.Property(expr, pi);
            type = pi.PropertyType;
        }
        return Expression.Lambda<Func<T, TValue>>(expr, arg).Compile();
    }
}



Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum