Was ist der Unterschied zwischen expression.bind und expression.assign (oder: was ist das Besondere an MemberBinding)?

c# expression-trees

Frage

Also, das ist in die Schrauben und Muttern, aber ich hoffe, dass jemand hier eine Einsicht haben kann.

Hier ist, was ich geschafft habe zu sammeln (obwohl ich natürlich falsch liegen könnte, also bitte korrigiert mich)

Ausdruck.Bind

Basierend auf meinen Nachforschungen, einschließlich des MSDN-Eintrags zu [topic] [1], scheint es, dass die Expression.Bind-Methode verwendet wird, um einen Ausdruck vom Typ MemberAssignment zu generieren, der ein bestimmter Subtyp von MemberBinding-Ausdrücken ist. Die Methode verwendet MemberInfo und den Ausdruck, an den sie gebunden werden soll. Der resultierende MemberAssignment-Ausdruck repräsentiert die Initialisierung eines Members.

Ausdruck.Assign

Ist eine Methode, die eine BinaryExpression erstellt, die eine Zuweisungsoperation darstellt

Hier sind meine Fragen: Warum können wir nicht Expression.Assign verwenden, wenn wir Bindungen an die Expression.MemberInit-Methode liefern? Anstatt von:

    MemberAssignment binding = Expression.Bind(PropAccessMethodInfo, TargetExpression)

Zum Beispiel könnten wir einfach folgendes tun:

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

Ich weiß, dass der Compiler sich beschweren wird, aber ich frage mich, ob es hier mehr als nur ein syntaktisches Problem gibt. Anders gesagt, geben Expression.Bind / MemberBindings uns etwas unter der Haube? Oder sind sie nur syntaktischer Zucker, der es einfacher macht, Mitgliederinitialisierungen zu verwalten?

Könnte es sogar noch einfacher sein, die Beziehung zwischen Geschäftsobjekten und den zugrunde liegenden Entitäten in .NET EF4 zu verfolgen? Passt es irgendwie in "Arbeiten mit Proxies" im Entity Framework oder hilft es bei der Änderungsverfolgung oder der Brücke zwischen dem Business-Objekt und der EF-basierten Datenzugriffsschicht?

Wie Sie wahrscheinlich voraussehen können, versuche ich programmatisch die Erstellung meiner Geschäftsobjekte von der zugrunde liegenden Komponente Entities zu verdrahten, und solche Ausdrücke (insbesondere die MemberInit-Methode) können mit dem Erstellungsbit sicher helfen.

Ich war mir jedoch nicht sicher, ob EF / .NET schlau genug ist, diese Bindungen für das Tracking zu verwenden. Oder, ob ich dieselben Bindungen zu der Biz <-> Ent Tracking / Brücke wiederverwenden könnte.

Ich hoffe, das ergab einen Sinn. Wenn etwas unklar ist, würde ich gerne mehr Informationen geben.

Vielen Dank!!

Akzeptierte Antwort

Der Objektinitialisierer ist ein komplexer syntaktischer Zucker ... Es ist nur syntaktischer Zucker, weil es auf der IL-Ebene nichts gibt, was einem Objektinitialisierer ähnelt ... Es gibt keine speziellen Anweisungen ... Nur der C # -Compiler schreibt die Anweisungen in einen bestimmten Reihenfolge, aber der gleiche Code könnte geschrieben werden, ohne die OI zu verwenden. Leider wird selten die genaue Funktionsweise dieses Zuckers beschrieben.

Und jetzt zeige ich dir, warum es komplexer syntaktischer Zucker ist!

Du könntest denken, dass dies:

foo = new Foo { Bar = 5 };

ist übersetzt in

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

Aber in Wahrheit weißt du, dass es nicht ... Sagen wir, dass foo eine Eigenschaft ist ... Sicherlich wird nicht zweimal darauf zugegriffen (ein Schreibvorgang zum Speichern des new Foo() und ein Lesezugriff für foo.Bar ). ..

Also könnte / sollte der Code gleich sein:

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

aber in Wahrheit ist es nicht! Es ist wahrscheinlich ähnlicher

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

Der Unterschied ist, dass foo nicht gesetzt ist, wenn der Setter von Foo.Bar eine Ausnahme Foo.Bar !

Sie können es unter http://ideone.com/PjD7zF ausprobieren

Der Quellcode:

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

Dieser nicht so unterscheidbare Unterschied, der zu der Komplexität des syntaktischen Zuckers hinzugefügt wird, ist sicherlich mehr als genug, um einen "speziellen" Expression zu erzeugen, der nur für diesen (der Expression.MemberInit ) erstellt wird.

Die Expression.Bind ist dann notwendig, weil sie genau das Arbeiten des Objektinitialisierers simulieren wollte, und im Objektinitialisierer haben Sie keinen Zugriff auf das "This" des neuen Objekts. Du kannst nicht schreiben:

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

und sie wollten nicht, dass du Ausdrücke wie schreiben kannst

foo = new Foo { somethingElse.Prop = 10 } 

das wäre möglich, wenn das Expression.MemberInit einfach ein Array von Expression.Assign akzeptiert.

Zweiter Punkt ... Expression.MemberInit ist in .NET 3.5 (und es ist notwendig, weil die Elementinitialisierung in LINQ ziemlich verwendet wird), Expression.Assign gibt es nur in .NET 4.0. Ausdrucksbäume wurden für LINQ erstellt und erstellt (mindestens LINQ minus LINQ to Objects, da LINQ to Objects keine Ausdrucksbäume verwendet). Expression.Assign war nicht notwendig, daher wurde es nicht implementiert. Expression.MemberInit war notwendig, also wurde es implementiert.

Dritter Punkt ... member initialization ist etwas, das in LINQ ziemlich üblich ist, also war es sicherlich schwieriger, alle Ausdrücke aus den "base" -Teilen der Member-Initialisierung (eine temporäre Variable, einige Zuweisungen, ...) zu erstellen (und diese Ausdrücke, wenn Debugging sicherlich komplexer war als eine vordefinierte Methode "All-in-One".

Vierter Punkt ... LINQ-Provider implementieren normalerweise nicht alle möglichen Expression und müssen oft mit der Tatsache arbeiten, dass der Ausdruck in einer völlig anderen Umgebung remote ausgeführt wird (siehe zum Beispiel LINQ-to- SQL, die LINQ-Abfragen auf einem SQL Server ausführt. Expression.MemberInit ist viel begrenzter als Expression.Assign , daher ist es einfacher, von einem LINQ-Provider implementiert zu werden.

Also wähle einige dieser Gründe ... Sie sind alle ziemlich gut und wahrscheinlich würde jeder von ihnen allein ausreichen, um eine Expression.MemberInit zu implementieren. Vier von ihnen zusammen?



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