Quelle est la différence entre expression.bind et expression.assign (ou: quelle est la particularité de MemberBinding)

c# expression-trees

Question

Donc, cela entre dans les rouages, mais j'espère que quelqu'un ici pourra avoir un aperçu.

Voici ce que j'ai réussi à rassembler (bien que, bien sûr, je puisse me tromper, alors corrigez-moi)

Expression.Bind

D'après mes recherches, y compris l'entrée MSDN sur la [rubrique] [1], il semble que la méthode Expression.Bind est utilisée pour générer une expression de type MemberAssignment, qui est un sous-type spécifique d'expressions MemberBinding. La méthode prend MemberInfo et l'expression à laquelle elle devrait être liée. L'expression MemberAssignment résultante représente l'initialisation d'un membre.

Expression.Assign

Est une méthode qui crée une BinaryExpression qui représente une opération d’affectation

Voici mes questions: Pourquoi ne pouvons-nous pas simplement utiliser Expression.Assign lors de la fourniture de liaisons à la méthode Expression.MemberInit? Au lieu de:

    MemberAssignment binding = Expression.Bind(PropAccessMethodInfo, TargetExpression)

Par exemple, nous pourrions simplement faire ce qui suit:

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

Je sais que le compilateur va se plaindre, mais je suppose que je demande s'il y a plus qu'un problème de syntaxe ici. En d'autres termes, Expression.Bind / MemberBindings nous donne-t-il quelque chose de plus sous le capot? Ou s'agit-il simplement d'un sucre syntaxique qui facilite la gestion des initialisations de membres?

Plus précisément encore, pourrait-il de toute façon aider à garder une trace de la relation entre les objets métier et les entités sous-jacentes dans .NET EF4? Cela convient-il en quelque sorte à "Utilisation de proxies" dans Entity Framework ou aide-t-il au suivi des modifications ou au pont entre l'objet métier et la couche d'accès aux données basée sur EF?

Comme vous pouvez probablement le prévoir, j'essaie de connecter par programmation la création de mes objets métier à partir du composant sous-jacent Entities et de telles expressions (en particulier la méthode MemberInit) peuvent aider à la création.

Cependant, je ne savais pas si EF / .NET était suffisamment intelligent pour utiliser ces liaisons pour effectuer le suivi? Ou bien si je pouvais réutiliser ces mêmes liaisons dans le suivi / pont Biz <-> Ent.

J'espère que cela a du sens. Si quelque chose n'est pas clair, je serais heureux de donner plus d'informations.

Merci!!

Réponse acceptée

L'initialiseur d'objet est un sucre syntaxique complexe ... Ce n'est que du sucre syntaxique car au niveau IL, rien ne ressemble à un initialiseur d'objet ... Il n'y a pas d'instructions spéciales ... Seul le compilateur C # écrit les instructions dans un certain ordre, mais le même code pourrait être écrit sans utiliser l’OI. Malheureusement, le fonctionnement exact de ce sucre est rarement décrit.

Et maintenant je vais vous montrer pourquoi c'est du sucre syntaxique complexe !

Vous pourriez penser que ceci:

foo = new Foo { Bar = 5 };

est traduit en

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

Mais en vérité, vous savez que ce n'est pas ... Disons que foo est une propriété ... On n'y accède pas deux fois (une écriture pour enregistrer le new Foo() et une lecture pour accéder à foo.Bar ). ..

Donc, le code pourrait / devrait être égal à ceci:

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

mais en vérité ça ne l'est pas! C'est probablement plus semblable à

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

La différence est que si le passeur de Foo.Bar lève une exception, foo n'est pas réglé!

Vous pouvez l'essayer sur: http://ideone.com/PjD7zF

Le code source:

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

Cette différence, qui s’ajoute à la complexité du sucre syntaxique, est certainement plus que suffisante pour créer une Expression "spéciale" construite uniquement pour cela ( Expression.MemberInit ).

Le Expression.Bind est alors nécessaire, car ils voulaient simuler exactement le fonctionnement de l'initialiseur d'objet, et dans l'initialiseur d'objet, vous n'avez pas accès au "ceci" du nouvel objet. Vous ne pouvez pas écrire:

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

et ils ne voulaient pas que vous puissiez écrire des expressions comme

foo = new Foo { somethingElse.Prop = 10 } 

cela serait possible si Expression.MemberInit acceptait simplement un tableau de Expression.Assign .

Deuxième point ... Expression.MemberInit est dans .NET 3.5 (et il est nécessaire car l'initialisation des membres est quelque chose de très utilisé dans LINQ), Expression.Assign est uniquement dans .NET 4.0. Les arbres d'expression sont nés et construits pour LINQ (au moins le LINQ moins le LINQ to Objects, car le LINQ to Objects n'utilise pas les arbres d'expression). Expression.Assign n'était pas nécessaire et n'a donc pas été implémenté. Expression.MemberInit était nécessaire, il a donc été implémenté.

Troisième point ... L’initialisation des membres est quelque chose de très courant dans LINQ, il a donc été sans doute plus difficile de construire toutes les expressions à partir des parties "de base" de l’initialisation des membres (une variable temporaire, certaines affectations, ...). les expressions lors du débogage étaient sûrement plus complexes) que d'avoir une méthode prédéfinie "tout-en-un".

Quatrième point ... Normalement, les fournisseurs LINQ n’implémentent pas toutes les Expression possibles et doivent souvent tenir compte du fait que l’expression sera exécutée à distance dans un environnement complètement différent (voir par exemple LINQ-to- SQL qui exécute les requêtes LINQ sur un serveur SQL). Expression.MemberInit est beaucoup plus limité que Expression.Assign . Il est donc plus facile à implémenter par un fournisseur LINQ.

Alors, choisissez quelques-unes de ces raisons ... Elles sont toutes plutôt bonnes et probablement qu’un seul suffirait pour décider de mettre en oeuvre un Expression.MemberInit . Quatre d'entre eux ensemble?



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow