对象初始化表达式是语言集成查询的一个重要功能,因为它们允许在仅允许表达式的上下文(如 λ 表达式和表达式树)中生成新的结构化值。例如,请考虑以下查询表达式,它为输入序列中的每个值创建了新的 Person 值:
对象初始化语法也可以方便地用于初始化结构化值的数组。例如,请考虑以下数组变量,该变量是使用单个的对象初始值设定项来初始化的:IEnumerable expr = names.Select(s => new Person { Name = s, Age = 21, CanCode = s.Length == 5 });
结构化值和类型static Person[] people = { new Person { Name="Allen Frances", Age=11, CanCode=false }, new Person { Name="Burke Madison", Age=50, CanCode=true }, new Person { Name="Connor Morgan", Age=59, CanCode=false }, new Person { Name="David Charles", Age=33, CanCode=true }, new Person { Name="Everett Frank", Age=16, CanCode=true }, };
LINQ 项目支持以数据为中心的编程样式,其中,某些类型的存在主要是为了通过结构化值提供静态“形式”,而不是提供同时具有状态和行为的完整对象。根据它的逻辑结论推测,通常,开发人员所关心的只是值的结构,以及对命名类型的需要,因为该形式很少使用。这就引出了对匿名类型 的介绍,匿名类型允许将新的结构定义为与它们的初始化进行“内联”。
在 C# 中,匿名类型的语法与对象初始化语法完全相同(除了省略了类型的名称)。例如,请考虑以下两个语句:
变量 v1 和 v2 都指向一个内存中对象,该对象的 CLR 类型有三个公共属性 — Name、Age 和 CanCode。变量的不同之处在于,v2 引用了匿名类型 的实例。在 CLR 术语中,匿名类型与任何其他类型没有区别。匿名类型的特殊之处在于,它们在编程语言中没有有意义的名称 — 创建匿名类型实例的唯一方法就是使用上述语法。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 };
要使变量能够引用匿名类型的实例,同时仍然从静态类型获益,C# 引入了 var 关键字,以便用于替换局部变量声明的类型名称。例如,请考虑以下合法的 C# 3.0 程序:
var 关键字会告诉编译器,从用于初始化变量的表达式的静态类型推断出变量的类型。在本例中,s、n 和 b 的类型分别是 string、int 和 bool。该程序与以下程序完全相同:var s = "Bob"; var n = 32; var b = true;
var 关键字方便用于其类型名称有意义的变量,但对于引用匿名类型实例的变量而言是必需的。string s = "Bob"; int n = 32; bool b = true;
在上述示例中,变量 value 是匿名类型,其定义与以下伪 C# 等效:var value = new { Name = "Chris Smith", Age = 31, CanCode = false };
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; } } }