匿名类型提供了一种方便的方法,可将一组只读属性封装到一个对象中,而无需先明确定义类型。
类型名称由编译器生成,在源代码级不可用。每个属性的类型由编译器推断。
匿名类的语法是 :
VAR o := CLASS { Name := "test", Value := "something" }
在 LINQ 中,这可能会导致
VAR result := from c in db.Customers where c.Orders.Count > 0 ;
select CLASS{ ID := C.CustomerID, Name := C.CustomerName, OrderCount := C.Orders.Count}
在这种情况下,对象将具有 ID、Name 和 OrderCount 属性(均已明确给出)。
如果从另一个对象中选择已命名的属性,可以省略 <Name> := 部分。在这种情况下,编译器将使用相同的名称:
VAR result := from c in db.Customers where c.Orders.Count > 0 ;
select CLASS{ C.CustomerID, C.CustomerName, OrderCount := C.Orders.Count}
在这种情况下,匿名类将具有 CustomerID、CustomerName(继承自 C)和 OrderCount(明确给出)属性。
匿名类型包含一个或多个公共只读属性。其他类型的类成员(如方法或事件)均无效。
用于初始化属性的表达式不能为 null、匿名函数或指针类型。
最常见的情况是使用其他类型的属性初始化匿名类型。在下面的示例中,假设存在一个名为 Product 的类。类 Product 包含颜色和价格属性,以及其他你不感兴趣的属性。变量 products 是 Product 对象的集合。匿名类型声明以 new 关键字开始。声明初始化了一个新类型,该类型只使用 Product 的两个属性。这样,在查询中返回的数据量就会减少。
如果不在匿名类型中指定成员名称,编译器将赋予匿名类型成员与用于初始化它们的属性相同的名称。必须为使用表达式初始化的属性提供名称,如上例所示。在下面的示例中,匿名类型的属性名称是 Color 和 Price。
var productQuery := ;
from prod in products ;
select CLASS { prod:Color, prod:Price }
foreach var v in productQuery
Console.WriteLine("Color={0}, Price={1}", v:Color, v:Price)
next
通常情况下,当使用匿名类型初始化变量时,会使用 var 将变量声明为隐式类型的局部变量。 类型名称不能在变量声明中指定,因为只有编译器可以访问匿名类型的底层名称。
匿名类型是直接派生自对象的 Class 类型,除 object 外不能被转换为其他类型。编译器会为每个匿名类型提供一个名称,但你的应用程序无法访问它。从公共语言运行时(CLR)的角度来看,匿名类型与任何其他引用类型没有区别。
如果程序集中的两个或多个匿名对象初始化程序指定了一系列顺序相同、名称和类型相同的属性,编译器就会将这些对象视为同一类型的实例。它们共享编译器生成的相同类型信息。
不能将字段、属性、事件或方法的返回类型声明为匿名类型。同样,也不能将方法、属性、构造函数或索引器的形参声明为匿名类型。要将匿名类型或包含匿名类型的集合作为参数传递给方法,可以将参数声明为 object 类型。然而,这样做违背了强类型的目的。如果必须存储查询结果或将其传递到方法边界之外,请考虑使用普通的命名结构体或类,而不是匿名类型。
由于匿名类型上的 Equals 和 GetHashCode 方法是根据属性的 Equals 和 GetHashCode 方法定义的,因此只有当它们的所有属性都相等时,同一匿名类型的两个实例才相等。