




匿名类型只能用var声明,本质是编译器生成的只读类,作用域限于当前方法,属性名大小写敏感且不可修改,相等性基于所有属性值但数组等引用类型不深度比较。

匿名类型的本质是编译器自动生成的只读类,没有公开的类型名。试图写 AnonymousType x = new { Name = "a", Age = 25 }; 会编译失败——AnonymousType 根本不存在。必须用 var 让编译器推断类型:var person = new { Name = "a", Age = 25 };
常见错误:在方法返回值、字段或参数中尝试使用匿名类型,比如 public {string Name} GetData() 或 private {int Id} _cache;,这些全部非法。匿名类型作用域仅限于当前方法内,且不能跨方法传递(除非转成 object,但会丢失编译时成员访问能力)。
写 new { Name = "Tom", age = 18 } 会生成两个属性:Name(string)和 age(int),注意 age 是小写,调用时也必须写 obj.age,写 obj.Age 就报错 CS1061 'object' does not contain a definition for 'Age'。
以下情况会触发新类型生成(即两个匿名对象不兼容):
new { A = 1, B = 2 } 和 new { B = 2, A = 1 } 是不同类型new { X = 5 }(int) vs new { X = 5L }(long)new { Id = 1 } 和 new { Id = 1, Name = "x" }
匿名类型最自然的使用场景是 LINQ 的 Select 投影,例如:
var result = users.Where(u => u.IsActive)
.Select(u => new { u.Id, u.Name, IsAdult = u.Age >= 18 });
这时 result 是 IEnumerable,每个元素都是同一匿名类型,可正常遍历和访问属性。
但要注意:
JsonSerializer.Serialize(obj) 可能抛 NotSupportedException,取决于序列化器配置)obj.GetType().GetProperties(),不能硬编码类型(string Name, int Age))、记录类型(record)或普通类两个匿名对象只要所有属性名、类型、值都相同,== 或 .Equals() 就返回 true。例如:
var a = new { X = 1, Y = "hello" };
var b = new { X = 1, Y = "hello" };
Console.WriteLine(a.Equals(b)); // true
但一旦任一属性为 null,且另一方对应属性是非空引用类型,比较仍成立;而如果双方都是 null,也没问题。真正容易出错的是嵌套匿名类型或含数组时——数组本身不会被深度比较,new { Data = new[] { 1, 2 } }.Equals(new { Data = new[] { 1, 2 } }) 返回 false,因为数组引用不同。
所以别依赖匿名类型的相等性做关键逻辑判断,尤其涉及集合去重或缓存键计算时,优先用明确的 record 或自定义 IEquatable 实现。