C# 匿名类型

8 阅读1分钟

C# 匿名类型

匿名类型提供了一种快捷方式,将一组只读属性封装到单个对象中,无需先显式定义命名类型。类型名称由编译器在编译时生成,源代码中无法直接访问。

  1. 声明与推断:一行代码创建数据容器
  2. LINQ 投影:匿名类型的主要阵地
  3. 值相等性:相同属性自动相等
  4. 匿名类型 vs 元组:什么时候选哪个

一、基本声明

使用 new 运算符与对象初始值设定项结合来创建。

var person = new { Name = "Alice", Age = 30 };
Console.WriteLine($"{person.Name} is {person.Age} years old.");
// 输出:Alice is 30 years old.

推断的属性名称

当使用变量或成员访问表达式初始化时,编译器自动推断属性名称:

string productName = "Laptop";
decimal price = 999.99m;

var product = new { productName, price };
Console.WriteLine($"{product.productName}: {product.price:C}");
// 输出:Laptop: $999.99

代码解析: 不需要写 Name = productName——编译器从变量名推断属性名为 productName

必须使用 var

由于类型名称不可见,必须使用 var 来声明:

var person = new { Name = "Alice", Age = 30 };
// 不能写成:AnonymousType person = ...  // 类型名不存在

二、在 LINQ 查询中使用

匿名类型最常见的用途是 LINQ 的 select 子句中的投影——只提取需要的属性子集。

var words = new[] { "apple", "blueberry", "cherry" };

var results = words.Select(w => new { Word = w, Length = w.Length });

foreach (var item in results)
    Console.WriteLine($"{item.Word} has {item.Length} letters.");

// 输出:
// apple has 5 letters.
// blueberry has 9 letters.
// cherry has 6 letters.

划重点: 这是匿名类型的核心场景。你不需要为每个查询结果定义一个 DTO 类——一行代码搞定。

三、相等性

具有相同属性名称和类型的两个匿名类型实例会共享编译器生成的同一类型。编译器自动重写 EqualsGetHashCode,基于属性值比较:

var a = new { Name = "Alice", Age = 30 };
var b = new { Name = "Alice", Age = 30 };
var c = new { Name = "Bob", Age = 25 };

Console.WriteLine(a.Equals(b));  // True —— 相同类型、相同值
Console.WriteLine(a.Equals(c));  // False —— 值不同

注意: 属性名称相同但顺序不同的匿名类型被视为不同类型——编译器按照属性名和顺序生成类型。

四、嵌套匿名类型

匿名类型可以包含其他匿名类型:

var order = new
{
    OrderId = 1,
    Customer = new { Name = "Alice", City = "Seattle" },
    Total = 150.00m
};

Console.WriteLine($"Order {order.OrderId} for {order.Customer.Name} in {order.Customer.City}");
// 输出:Order 1 for Alice in Seattle

五、特性总结

特性
编译器生成类型internal sealed class,派生自 Object
属性可访问性所有属性 public只读
支持的结构with 表达式、基于值的 Equals/GetHashCode/ToString
表达式树✅ 支持(元组不支持)

局限性

  • 不能作为方法返回类型、方法参数或字段类型(无法命名)
  • 作用域限定在声明它们的方法内
  • 不能添加方法、事件或自定义运算符
  • 属性始终只读

六、匿名类型 vs 元组

维度匿名类型元组
类型类别引用类型值类型
作用域方法内可跨方法
表达式树✅ 支持❌ 不支持
解构
属性可写性只读可读写(默认)
语法简洁度new { ... }(...)
推荐场景LINQ to SQL / 表达式树新代码的通用数据分组

决策建议: 对于大多数新代码,优先使用元组——性能更好、语法更简洁、支持解构。只在与表达式树(Entity Framework 的 Select 投影、动态 LINQ 查询)打交道时,匿名类型仍是不可替代的。

最后

匿名类型像是一个"方法内部的 DTO"。在 LINQ 查询中投影中间结果时非常方便,但一出了方法边界就无能为力。新代码中元组已经替代了匿名类型的大部分场景,把它当作 LINQ 投影的专用工具就好。