expression.bind和expression.assign之间有什么区别(或者:关于MemberBinding的特殊之处)

时间:2013-08-05 07:13:19

标签: c# expression-trees

所以,这是进入细节的,但我希望这里有人可以有洞察力。

这是我设法收集的内容(当然,我可能错了任何一个,所以请纠正我)

  

Expression.Bind

     
    

根据我的研究,包括[主题] [1]上的MSDN Entry,似乎Expression.Bind方法用于生成MemberAssignment类型的表达式,这是MemberBinding表达式的特定子类型。该方法采用MemberInfo和它应该绑定的表达式。生成的MemberAssignment表达式表示成员的初始化。

  
     

Expression.Assign

     
    

是一个创建表示赋值操作的BinaryExpression的方法

  

这是我的问题:为什么我们只能在提供Expression.MemberInit方法的绑定时使用Expression.Assign?而不是:

    MemberAssignment binding = Expression.Bind(PropAccessMethodInfo, TargetExpression)

例如,我们可以执行以下操作:

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

我知道编译器会抱怨,但我想我在问这里是否还有一个语法问题。 换句话说,Expression.Bind / MemberBindings是否为我们提供了额外的东西?或者,它们只是语法糖,使管理成员初始化变得更容易吗?

更具体地说,它是否可以帮助跟踪业务对象与.NET EF4中的底层实体之间的关系?它是否适合实体框架中的“使用代理”或帮助进行更改跟踪或业务对象与基于EF的数据访问层之间的桥梁?

正如您可能预见的那样,我正在尝试以编程方式从底层组件Entities连接我的业务对象的创建,这样的表达式(尤其是MemberInit方法)可以帮助确保创建位。

但是,我不确定EF / .NET是否足够聪明,可以使用这些绑定来进行跟踪?或者,我是否可以将这些相同的绑定重用于Biz< - > Ent tracking / bridge。

我希望这是有道理的。如果有什么不清楚的地方,我很乐意提供更多信息。

谢谢!

1 个答案:

答案 0 :(得分:9)

对象初始化器是一个复杂的语法糖......它只是语法糖,因为在IL级别没有任何类似于对象初始化器......没有特殊指令......只有C#编译器写入指令按某种顺序,但可以在不使用OI的情况下编写相同的代码。可悲的是,这种糖的确切工作很少被描述。

现在我会告诉你为什么它是复杂的语法糖!

你可以这样想:

foo = new Foo { Bar = 5 };

被翻译为

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

但事实上你知道它不是......让我们说foo是一个属性......当然它不被访问两次(一次写入用于保存new Foo()和一个阅读以访问foo.Bar)...

所以代码可以/应该等于这个:

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

但事实并非如此!它可能更类似于

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

不同之处在于,如果Foo.Bar的setter引发异常,则不会设置foo

您可以尝试:http://ideone.com/PjD7zF

源代码:

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

这种不那么明显的区别,加上语法糖的复杂性,肯定足以创建一个仅为此构建的“特殊”ExpressionExpression.MemberInit

然后需要Expression.Bind,因为他们想要精确模拟对象初始值设定项的工作,并且在对象初始化程序中,您无法访问新对象的“this”。你不能写:

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

并且他们不希望您能够编写像

这样的表达式
foo = new Foo { somethingElse.Prop = 10 } 
如果Expression.MemberInit只是接受Expression.Assign的数组。

,那么

就有可能

第二点...... Expression.MemberInit在.NET 3.5中(并且它是必要的,因为成员初始化是LINQ中常用的东西),Expression.Assign仅在.NET 4.0中。表达式树是为LINQ而生成和构建的(至少LINQ减去对象的LINQ,因为LINQ to Objects不使用表达式树)。 Expression.Assign没有必要,所以没有实施。 Expression.MemberInit是必要的,因此已实施。

第三点......成员初始化在LINQ中很常见,因此必须从成员初始化的“基础”部分构建所有表达式(临时变量,一些赋值,......)肯定更难(并且这些表达式在调试时肯定更复杂)而不是预先构建的方法“all-in-one”。

第四点...... LINQ提供程序通常不会实现所有可能的Expression(s),并且经常需要处理表达式将在完全不同的环境中远程执行的事实(例如,参见在SQL Server上执行LINQ查询的LINQ-to-SQL。 Expression.MemberInitExpression.Assign更受限制,因此LINQ提供程序更容易实现。

所以选择其中一些原因......它们都非常好,可能其中任何一个单独就足以决定实施Expression.MemberInit。其中四个在一起?