3.5 结构型值和类型(Structured values and types)
LINQ 项目以数据为中心的程序设计格式(data-centric programming style),有些类型存在的主要目的是在结构型值之上(over a structured value)提供一个静态的“shape”(static "shape"),这胜过同时有声明(state)和行为(behavior)的完全成熟(full-blown)的对象。对它的逻辑结论(logical conclusion)作出这个假设(taking this premise),最常见的情形(is often the case)是所有的开发者当心(cares about)值的结构(structure of the value),对这个 shape 一个命名的类型(named type)的需要(the need for)是几乎无用的(is of little use)。这导致(leads to)匿名类型(anonymous types)的介绍(introduction)允许新的结构(structures)给它们的初始化(initialization)定义成“inline”。
在 C# 语言里,匿名类型(anonymous types)与对象初始化的语法(object initialization syntax)是一样的(is identical to),除了类型的名字(name of the type)被忽略(omitted)了。例于,考察下面的两段语句:
object v1 = new Person {
Name = "Chris Smith", Age = 31, CanCode = false
};
object v2 = new { // note the omission of type name
Name = "Chris Smith", Age = 31, CanCode = false
};
变量 v1 和 v2 都指向一个内存中的对象(in-memory object),它的 CLR 类型有三个公共的属性(public properties):Name,Age,和CanCode。它们的不同之处(differ in)是 v2 引用自(refers to)一个匿名类型(anonymous types)的实体。在 CLR 中(in CLR terms),匿名类型与任何其他类型没有什么不同。使得匿名类型特殊(special)的是它们在你的程序开发语言中没有特殊意义的名字(have no meaningful name)。创建一个匿名类型的实体(instances of an anonymous type)的唯一途经就是使用上面所示的语法。
为了允许变量引用匿名类型的实体仍然需要利用(benefit from)静态类型(static typing),C# 介绍 var 关键词时说它可以用于局部变量定义(local variable declarations)的类型名称(type name),例于,考察下面这段合法(legal)的 C# 3.0 程序:
var s = "Bob";
var n = 32;
var b = true;
关键词 var 告诉编译器从用来初始化变量的表达式的静态类型(static type)来推断(infer)这个变量的类型。在这个例子中,变量 s,n 和 b 的类型分别(respectively)是 string,int 和 bool,这段程序和下面的程序是一样的:
string s = "Bob";
int n = 32;
bool b = true;
关键词 var 对那些类型有特殊含义名称(meaningful names)的变量是一个方便的工具(convenience),但是它对引用自匿名类型的实体的变量(instances of anonymous types)却是必需的(necessity)。
var value = new {
Name = "Chris Smith", Age = 31, CanCode = false
};
在上面的例子中,变量 value 是一个匿名类型,它的定义与下面的 C# 伪代码(pseudo-C#:)是相等的(equivalent to):
internal class ??? {
string _Name;
int _Age;
bool _CanCode;
public string Name {
get { return _Name; } set { _Name = value; }
}
public int Age{
get { return _Age; } set { _Age = value; }
}
public bool CanCode {
get { return _CanCode; } set { _CanCode = value; }
}
}
匿名类型不能被共享于(shared)跨越(across)汇编后代码的边界(assembly boundaries),但是编译保证(ensures)在每段汇编代码中(within each assembly)对被赋予的(given)成 name/type 对(name/type pairs)的属性序列(sequence of property)最多只有一个匿名类型。
因为匿名类型常常被用在从一个现有的结构型值(an existing structured value)中选取它的一个或更多的成员的投影(projections)中,这样我们就能够简单地在一个匿名类型的初始化(initialization of an anonymous type)中从另一个值引用(reference)域(fields)或属性(properties)。这导致(results in)匿名类型获取到一个属性(property),它的名称(name),类型(type)和值(value)都拷贝自引用的属性或域(referenced property or field)。
举例来说,下面的程序通过从其他值中结合属性(combining properties)来创建一个新的结构性值(structured value):
var bob = new Person { Name = "Bob", Age = 51, CanCode = true };
var jane = new { Age = 29, FirstName = "Jane" };
var couple = new {
Husband = new { bob.Name, bob.Age },
Wife = new { Name = jane.FirstName, jane.Age }
};
int ha = couple.Husband.Age; // ha == 51
string wn = couple.Wife.Name; // wn == "Jane"
上面展示的引用域(fields)或属性(properties)的代码是与下面写出的更明显的形式(more explicit form)的一种更简单方便的语法(is simply a convenient syntax):
var couple = new {
Husband = new { Name = bob.Name, Age = bob.Age },
Wife = new { Name = jane.FirstName, Age = jane.Age }
};
在两种形式中(in both cases),变量 couple 都从变量 bob 和 jane 中的 Name 和 Age 属性获取了一份自己的 copy 。
匿名类型通常用于查询的 select 语句(select clause),例于,看下面的查询程序:
var expr = people.Select(p => new {
p.Name, BadCoder = p.Age == 11
});
foreach (var item in expr)
Console.WriteLine("{0} is a {1} coder",
item.Name,
item.BadCoder ? "bad" : "good");
在这个例子中我们能够在类型 Person 上创建一个新的投影(projection),它正确地符合(exactly matched)我们的执行代码(processing code)所需要的形态(shape),但是仍然具有静态类型(static type)的好处(the benefits of)。
待续, 错误难免,请批评指正,译者Naven 2005-10-23