对象初始化器是一个复杂的语法糖...它只是语法糖,因为在 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
. 他们四个在一起?