Albero delle espressioni: come ottenere l'istanza di dichiarazione?

c# expression-trees lambda

Domanda

Sono un principiante quando si tratta di alberi di espressione, quindi non sono sicuro di come fare questa domanda o quale terminologia usare. Ecco una versione semplificata di ciò che sto cercando di fare:

Bar bar = new Bar();
Zap(() => bar.Foo);

public static void Zap<T>(Expression<Func<T>> source)
{
   // HELP HERE:
   // I want to get the bar instance and call bar.Zim() or some other method.
}

Come posso arrivare al bar all'interno del metodo Zap?

Risposta accettata

Poiché l'espressione passata nel metodo Zap è un albero, è sufficiente camminare sull'albero utilizzando un visitatore dell'albero dell'espressione e cercare la prima espressione ConstantExpression nell'espressione. Sarà probabilmente nella seguente sequenza:

(((source.Body as MemberExpression).Expression as MemberExpression).Expression as ConstantExpression).Value

Si noti che l'istanza della bar viene catturata da una chiusura, che viene implementata come una classe interna con l'istanza come membro, da cui proviene la seconda espressione membro.

MODIFICARE

Quindi devi ottenere il campo dalla chiusura generata in questo modo:

    static void Main(string[] args)
    {
        var bar = new Bar();
        bar.Foo = "Hello, Zap";
        Zap(() => bar.Foo);
    }

    private class Bar
    {
        public String Foo { get; set; }    
    }

    public static void Zap<T>(Expression<Func<T>> source)
    {
        var param = (((source.Body as MemberExpression).Expression as MemberExpression).Expression as ConstantExpression).Value;
        var type = param.GetType();
        // Note that the C# compiler creates the field of the closure class 
        // with the name of local variable that was captured in Main()
        var field = type.GetField("bar");
        var bar = field.GetValue(param) as Bar;
        Debug.Assert(bar != null);
        Console.WriteLine(bar.Foo);
    }

Risposta popolare

Se conosci il tipo di "barra", puoi farlo (sto riusando alcuni bit dalla risposta di codekaizen qui):

    static void Main(string[] args)
    {
        var bar = new Bar();
        bar.Foo = "Hello, Zap";
        Zap(() => bar.Foo);

        Console.ReadLine();
    }

    private class Bar
    {
        public String Foo { get; set; }
    }

    public static void Zap<T>(Expression<Func<T>> source)
    {
        var body = source.Body as MemberExpression;
        Bar test = Expression.Lambda<Func<Bar>>(body.Expression).Compile()();
        Console.WriteLine(test.Foo);
    } 

Nella maggior parte dei casi, è possibile trovare un'espressione che rappresenta il proprio oggetto all'interno di un albero di espressioni, quindi compilare ed eseguire questa espressione e ottenere l'oggetto (ma questa operazione non è molto veloce, tra l'altro). Quindi, il bit che mancava era il metodo Compile (). Puoi trovare un po 'più di informazioni qui: Come: Eseguire Expression Trees .

In questo codice, presumo che tu passi sempre un'espressione come "() => oggetto.Membro". Per uno scenario del mondo reale dovrai analizzare se hai un'espressione di cui hai bisogno (ad es. Lanciare un'eccezione se non è un'espressione membro). O usare ExpressionVisitor, che è un po 'complicato.

Di recente ho risposto a una domanda molto simile qui: Come posso iscrivermi a un evento di un oggetto all'interno di un albero di espressioni?



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é