C# 3.0语言新特性(语言规范):4 对象和集合初始化器

时间:2022-09-03 19:15:07
原文:《C# Version 3.0 Specification》,Microsoft
翻译:lover_P

一个对象创建表达式可以包含一个对象或集合初始化器,用于初始化新创建的对象的成员或新创建的集合的元素。

object-creation-expression:
new  type  (  argument-listopt  )  object-or-collection-initializeropt

new  type  object-or-collection-initializer

object-or-collection-initializer:
object-initializer
collection-initializer

一个对象创建表达式可以省略构造器参数列表,并将其连同圆括号一起替换为一个对象或集合初始化器。省略构造器参数列表并将其连同圆括号一起替换为一个对象或集合初始化器等价于指定一个空的参数列表。

在执行一个带有对象或集合初始化器的对象创建表达式时,首先调用实例构造器,然后执行对象或集合初始化器指定的成员或元素初始化。

对象或集合初始化器不能引用正在初始化的对象实例。

4.1 对象初始化器

对象初始化器指定了对象的一个或多个域或属性的值。

object-initializer:
{  member-initializer-listopt  }

{  member-initializer-list  ,  }

member-initializer-list:
member-initializer
member-initializer-list 
,  member-initializer

member-initializer:
identifier 
=  initializer-value

initializer-value:
expression
object-or-collection-initializer

对象初始化器由一系列的成员初始化器构成,包围在{}记号中,并用逗号进行分隔。每个成员初始化器以对象的一个可访问的域或属性的名字开始,后跟一个等号,之后是一个表达式或一个对象或集合初始化器。如果对象初始化其中包括了对同一个域或属性的多于一个的成员初始化器,将会发生错误。

在等号后面指定了表达式的成员初始化器的处理与域和属性的赋值一致。

在等号后面指定了对象初始化器的成员初始化器也是对一个嵌套对象的初始化。与为域或属性赋一个新值不同,对象初始化器中的赋值被视为对域或属性的成员进行赋值。一个具有值类型的属性不能通过这种构造来进行初始化。

在等号后面指定了集合初始化器的成员初始化器也是对一个嵌套集合的初始化。与为域或属性赋一个新的集合不同,初始化器中给定的元素将被添加到域或属性所引用的集合中。该域或属性必须是一个满足下一节所指定的需求的集合类型。

下面的类表是一个具有两个坐标值的点:

public class Point

{

    int x, y;

 

    public int X { get { return x; } set { x = value; } }

    public int Y { get { return y; } set { y = value; } }

}

Point的一个实例可以像下面这样创建和初始化:

var a = new Point { X = 0, Y = 1 };

其等价于:

var a = new Point();

a.X = 0;

a.Y = 1;

下面的类表是一个具有两个点的矩形:

public class Rectangle

{

    Point p1, p2;

 

    public Point P1 { get { return p1; } set { p1 = value; } }

    public Point P2 { get { return p2; } set { p2 = value; } }

}

可以像下面这样创建和初始化一个Rectangle

var r = new Rectangle {

    P1 = new Point { X = 0, Y = 1 },

    P2 = new Point { X = 2, Y = 3 }

};

其等价于:

var r = new Rectangle();

var __p1 = new Point();

__p1.X = 0;

__p1.Y = 1;

r.P1 = __p1;

var __p2 = new Point();

__p2.X = 2;

__p2.Y = 3;

r.P2 = __p2;

其中的__p1__p2是临时变量,在其他地方不可见也不可访问。

如果Rectangle的构造器分配了两个嵌套的Point实例:

public class Rectangle

{

    Point p1 = new Point();

    Point p2 = new Point();

 

    public Point P1 { get { return p1; } }

    public Point P2 { get { return p2; } }

}

下面的构造可以用来初始化内嵌的Point实例,而不是为其赋以新值:

var r = new Rectangle {

    P1 = { X = 0, Y = 1 },

    P2 = { X = 2, Y = 3 }

};

其等价于:

var r = new Rectangle();

r.P1.X = 0;

r.P1.Y = 1;

r.P2.X = 2;

r.P2.Y = 3;

4.2 集合初始化器

集合初始化器指定了集合的元素。

collection-initializer:
{  element-initializer-listopt  }

{  element-initializer-list  ,  }

element-initializer-list:
element-initializer
element-initializer-list 
,  element-initializer

element-initializer:
non-assignment-expression

一个集合初始化器由一系列的元素初始化器构成,包围在{}记号之间,并使用逗号进行分隔。每个元素初始化器指定一个元素,该元素将被添加到待初始化的集合对象中。为了避免与成员初始化器混淆,元素初始化器不能是赋值表达式。

下面是包含了集合初始化器的对象创建表达式的一个例子:

List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

可以应用集合初始化器的对象的类型必须实现了System.Collections.Generic.ICollections<T>并指定了确定的T。此外,必须存在从每个元素初始化器的类型到T的隐式转换。如果这些条件不能满足,就会产生一个编译期错误。集合初始化器将依次对每个指定的元素调用ICollection<T>.Add(T)

下面的类表是一个具有一个名字和一组电话号码的通讯录:

public class Contact

{

    string name;

    List<string> phoneNumbers = new List<string>();

 

    public string Name { get { return name; } set { name = value; } }

    public List<string> PhoneNumbers { get { return phoneNumbers; } }

}

可以像下面这样创建和初始化一个List<Contact>

var contacts = new List<Contact> {

    new Contact {

        Name = "Chris Smith",

        PhoneNumbers = { "206-555-0101", "425-882-8080" }

    },

    new Contact {

        Name = "Bob Harris",

        PhoneNumbers = { "650-555-0199" }

    }

};

其等价于:

var contacts = new List<Contact>();

var __c1 = new Contact();

__c1.Name = "Chris Smith";

__c1.PhoneNumbers.Add("206-555-0101");

__c1.PhoneNumbers.Add("425-882-8080");

contacts.Add(__c1);

var __c2 = new Contact();

__c2.Name = "Bob Harris";

__c2.PhoneNumbers.Add("650-555-0199");

contacts.Add(__c2);

其中__c1__c2是临时变量,在其他地方不可见且不可访问。