Qual è la differenza tra expression.bind e expression.assign (o: cosa è speciale di MemberBinding)

c# expression-trees

Domanda

Quindi, questo sta entrando nei pazzi, ma spero che qualcuno qui possa avere un'idea.

Ecco cosa sono riuscito a raccogliere (anche se, naturalmente, potrei sbagliarmi su qualsiasi cosa, quindi per favore correggimi)

Expression.Bind

Sulla base delle mie ricerche, inclusa la voce MSDN su [argomento] [1], sembra che il metodo Expression.Bind sia utilizzato per generare un'espressione di tipo MemberAssignment, che è un sottotipo specifico delle espressioni MemberBinding. Il metodo prende MemberInfo e l'espressione a cui deve essere associato. L'espressione MemberAssignment risultante rappresenta l'inizializzazione di un membro.

Expression.Assign

È un metodo che crea un'espressione Binary che rappresenta un'operazione di assegnazione

Ecco le mie domande: Perché non possiamo usare Expression.Assign quando fornisci binding al metodo Expression.MemberInit? Invece di:

    MemberAssignment binding = Expression.Bind(PropAccessMethodInfo, TargetExpression)

Ad esempio, potremmo fare solo quanto segue:

    MemberExpression getProperty = Expression.Property(FindObjectExpression, PropAccessMethodInfo)
    Expression binding = Expression.Assign(getProperty, TargetExpression)

So che il compilatore si lamenterà, ma suppongo che sto chiedendo se c'è più di un problema sintattico qui. In altre parole, Expression.Bind / MemberBindings ci offre qualcosa di aggiuntivo sotto il cofano? Oppure, sono solo zucchero sintattico che rende più facile gestire le intializzazioni dei membri?

Ancora più specificamente, potrebbe in ogni caso aiutare a tenere traccia della relazione tra gli oggetti aziendali e le entità sottostanti in .NET EF4? In qualche modo si adatta a "Lavorare con proxy" in Entity Framework o aiuta con il rilevamento delle modifiche o il ponte tra l'oggetto business e il livello di accesso ai dati basato su EF?

Come probabilmente puoi prevedere, sto cercando di collegare in modo programmatico la creazione dei miei oggetti di business dal componente sottostante. Entità e tali espressioni (in particolare, il metodo MemberInit) possono essere di aiuto con il bit di creazione.

Tuttavia, non ero sicuro che l'EF / .NET fosse abbastanza intelligente da utilizzare questi collegamenti per eseguire il tracciamento? Oppure, se potessi riutilizzare quegli stessi binding per Biz <-> Ent tracking / bridge.

Spero che abbia senso. Se qualcosa non è chiaro, sarei felice di dare ulteriori informazioni.

Grazie!!

Risposta accettata

L'inizializzatore dell'oggetto è uno zucchero sintattico complesso ... È solo zucchero sintattico perché a livello IL non c'è nulla che assomigli ad un inizializzatore di un oggetto ... Non ci sono istruzioni speciali ... Solo il compilatore C # scrive le istruzioni in un certo ordine, ma lo stesso codice potrebbe essere scritto senza utilizzare l'OI. Purtroppo raramente viene descritto il funzionamento esatto di questo zucchero.

E ora ti mostrerò perché è complesso zucchero sintattico!

Potresti pensare che questo:

foo = new Foo { Bar = 5 };

è tradotto a

foo = new Foo();
foo.Bar = 5;

Ma in realtà sai che non è ... Diciamo che foo è una proprietà ... Sicuramente non è accessibile due volte (una scrittura per salvare il new Foo() e una lettura per l'accesso a foo.Bar ). ..

Quindi il codice potrebbe / dovrebbe essere uguale a questo:

Foo tmp = new Foo();
foo = tmp;
tmp.Bar = 5;

ma in realtà non lo è! Probabilmente è più simile a

Foo tmp = new Foo();
tmp.Bar = 5;
foo = tmp;

La differenza è che se il setter di Foo.Bar lancia un'eccezione, foo non viene settato!

Puoi provarlo su: http://ideone.com/PjD7zF

Il codice sorgente:

using System;

class Program
{
    class Foo
    {
        public Foo()
        {
            Console.WriteLine("Building Foo");
        }

        public int Bar
        {
            get
            {
                Console.WriteLine("Getting Foo.Bar");
                return 0;
            }

            set
            {
                Console.WriteLine("Setting Foo.Bar: boom!");
                throw new Exception();
            }
        }
    }

    static Foo foo2;

    static Foo foo
    {
        get
        {
            Console.WriteLine("Getting foo");
            return foo2;
        }

        set
        {
            Console.WriteLine("Setting foo");
            foo2 = value;
        }
    }

    static void Main(string[] args)
    {
        try
        {
            foo = new Foo { Bar = 100 };

            // Not executed, only to disassemble and check 
            // that it isn't using special instructions!
            foo = new Foo();
            foo.Bar = 200;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception: {0}", ex);
        }

        Console.WriteLine("Finished try/catch");

        Console.WriteLine("foo initialized: {0}", foo != null);
    }
}

Questa differenza non tanto sottovalutata, aggiunta alla complessità dello zucchero sintattico, è sicuramente più che sufficiente per creare Expression "speciale" che è costruita solo per questo ( Expression.MemberInit ).

L' Expression.Bind è quindi necessario, perché volevano simulare esattamente il funzionamento dell'inizializzatore dell'oggetto e nell'inizializzatore dell'oggetto non si ha accesso al "questo" del nuovo oggetto. Non puoi scrivere:

// wrong
foo = new Foo { this.Bar = 5 };

e non volevano che tu fossi in grado di scrivere espressioni come

foo = new Foo { somethingElse.Prop = 10 } 

ciò sarebbe possibile se Expression.MemberInit accettasse semplicemente un array di Expression.Assign .

Secondo punto ... Expression.MemberInit è in .NET 3.5 (ed è necessario perché l'inizializzazione dei membri è qualcosa di abbastanza usato in LINQ), Expression.Assign è solo in .NET 4.0. Gli alberi di espressione sono nati e costruiti per LINQ (almeno il LINQ meno il LINQ agli oggetti, poiché il LINQ agli oggetti non utilizza gli alberi di espressione). Expression.Assign non era necessario, quindi non è stato implementato. Expression.MemberInit era necessario, quindi è stato implementato.

Terzo punto ... l'inizializzazione dei membri è qualcosa di abbastanza comune in LINQ, quindi dover costruire tutte le espressioni dalle parti "base" dell'inizializzazione del membro (una variabile temporanea, alcuni compiti, ...) è stato sicuramente più difficile (e questi le espressioni in fase di debug erano sicuramente più complesse) rispetto all'utilizzo di un metodo "all-in-one" predefinito.

Quarto punto ... I provider LINQ normalmente non implementano tutte le Expression possibili, e molto spesso devono lavorare con il fatto che l'espressione verrà eseguita in remoto su un ambiente completamente diverso (vedere ad esempio LINQ-to- SQL che esegue query LINQ su un server SQL). Expression.MemberInit è molto più limitato di Expression.Assign , quindi è più facile da implementare da un provider LINQ.

Quindi scegli alcuni di questi motivi ... Sono tutti abbastanza buoni, e probabilmente ognuno di loro da solo sarebbe sufficiente per decidere di implementare un Expression.MemberInit . Quattro di loro insieme?



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é