原文:《C# Version 3.0 Specification》,Microsoft
翻译:lover_P
C# 3.0允许将new运算符用于一个匿名对象初始化器来创建一个匿名类型的对象。
primary-no-array-creation-expression:
...
anonymous-object-creation-expression
anonymous-object-creation-expression:
new anonymous-object-initializer
anonymous-object-initializer:
{ member-declarator-listopt }
{ member-declarator-list , }
member-declarator-list:
member-declarator
member-declarator-list , member-declarator
member-declarator:
simple-name
member-access
identifier = expression
一个匿名对象初始化器声明了一个匿名类型并返回了该类型的一个实例。匿名类型是一个没有名字并且直接继承自object的类类型。匿名类型的成员是一系列可读/写属性,这些属性依次通过创建该类型的实例时使用的对象初始化器进行推断。特殊地,具有下面形式的一个匿名对象初始化器:
new { p1 = e1 , p2 = e2, ... pn = en }
声明了一个具有下面形式的匿名类型:
class __Anonymous1
{
private T1 f1;
private T2 f2;
...
private Tn fn;
public T1 p1 { get { return f1; } set { f1 = value; } }
public T2 p2 { get { return f2; } set { f2 = value; } }
...
public Tn pn { get { return fn; } set { fn = value; } }
}
其中的每个Tx是对应的表达式ex的类型。如果匿名对象初始化器中的某个表达式具有空类型,会发生一个编译期错误。
匿名类型的名字由编译器自动生成,并且不能在程序文本中引用。
在同一个程序中,两个具有相同名字、相同类型和相同顺序的属性的匿名对象初始化器将产生同一个匿名类型的实例。(这个定义包括了属性的顺序,这是因为在某些环境中顺序是可见的而且是非常重要的,比如反射。)
下面的例子:
var p1 = new { Name = "Lawnmower", Price = 495.00 };
var p2 = new { Name = "Shovel", Price = 26.95 };
p1 = p2;
其中最后一行中的赋值是允许的,因为p1和p2具有相同的匿名类型。
一个成员初始化器可以缩写为一个简单名字或一个成员访问。这时称该成员初始化器为发散性初始化器(Projection Initializer),也是对具有相同名字的属性的声明和赋值的简写。特别地,具有下面形式的成员声明器:
identifier
expr . identifier
与下面的对应形式完全等价:
identifier = identifier
identifier = expr . identifier
因此,在一个发散性的初始化器中,identifier同时选择了所赋的值的值和域或属性。直观上看,发散性的初始化器反映出的不仅仅是一个值,还包括这个值的名字。