9

所以,这涉及到具体细节,但我希望这里的人可能有见识。

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

表达式.绑定

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

表达式.赋值

是一种创建表示赋值操作的 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 的数据访问层之间的桥梁?

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

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

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

谢谢!!

4

1 回答 1

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;

不同之处在于,如果 setterFoo.Bar抛出异常,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);
    }
}

这种不那么微妙的差异,加上语法糖的复杂性,肯定足以创建一个Expression仅为此构建的“特殊”(the Expression.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 to Objects,因为 LINQ to Objects 不使用表达式树)。Expression.Assign没有必要,所以没有实施。Expression.MemberInit是必要的,所以它被实施了。

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

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

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

于 2013-08-05T14:59:48.873 回答