类型 · 值与引用
C# 里 struct 与 class
声明用的关键字不如赋值语义重要:class 变量持有的是指向同一个共享对象的引用;struct 变量持有的是它自己的一份拷贝。其他一切都由此而来。
class:两个名字,一个对象
class 变量赋值时拷贝的是引用,不是对象。两个名字指向同一个盒子:
class Dog
{
public string Name;
public Dog(string name) { Name = name; }
}
Dog a = new Dog("Rex");
Dog b = a; // b 是同一个对象的第二个名字
b.Name = "Max"; // 通过 b 改名……
Console.WriteLine(a.Name); // ……a 也跟着改名了
class Dog
{
public string Name;
public Dog(string name) { Name = name; }
}
Dog a = new Dog("Rex");
Dog b = new Dog("Rex"); // 一个真正的新对象
b.Name = "Max";
Console.WriteLine(a.Name); // 没被动过
Console.WriteLine(a == b); // == 比较引用:不同对象 → False
// (内容相同,身份不同)
struct:赋值即拷贝
struct 把语义反过来。给 struct 变量赋值会拷贝整个值——从那一刻起两个变量互不相干:
struct Point
{
public int X;
public Point(int x) { X = x; }
}
Point a = new Point(1);
Point b = a; // 完整拷贝——b 有自己的存储
b.X = 99;
Console.WriteLine(a.X); // 1 —— a 从未被碰过
Console.WriteLine(b.X); // 99
诚实说明:浏览器引擎目前把 struct 当 class 处理(引用语义)——这段程序在 Visual Studio 里会像注释所示打印 1 和 99。概念不变;在引擎长出值语义之前,拷贝行为请在真实编译器里验证。
这个差异解释的 gotcha
有没有过这种经历:通过一个变量改了字段,另一个变量竟然也"看到"了变化?那是引用别名。它的镜像:在方法里改了 struct 的字段,调用方的拷贝没变?那是值拷贝。两者都是各自语义下的正确行为——bug 是你期待了另一种:
| 场景 | class | struct |
|---|---|---|
b = a; 拷贝的是…… | 引用 | 整个值 |
通过 b 修改会影响 a 吗? | 会——同一个对象 | 不会——各自的拷贝 |
a == b 意味着…… | 同一身份 | 字段值相同(如果定义了 ==) |
| 住在哪里…… | 堆上,垃圾回收 | 内联(栈上或在宿主内部) |
到底选哪个?
大多数代码库遵循的实用规则:
- 默认用
class。有身份的实体(用户、订单、狗)——身份正是引用语义所建模的东西。 - 考虑
struct:小(约 16–24 字节以内)、不可变、拷贝廉价自然的值状数据:坐标、金额、日期、颜色。.NET 库自己的int、double、bool、DateTime、Guid全是 struct。 - 可变的 struct 是陷阱。一份拷贝悄悄改自己几乎从来不是本意——如果 struct 需要变,它多半想成为 class。
一分钟自测
去练习场跑这段,按运行之前先预测输出——就是上面的别名演示换了变量名:
Dog x = new Dog("One");
Dog y = x;
y = new Dog("Two");
Console.WriteLine(x.Name); // 预测:One 还是 Two?(引用 vs 对象)
如果你答"One"——把 new Dog("Two") 赋给 y 只是让 y 的引用改指向,它从不碰 x 指着的那个对象。相关阅读:var 与显式类型。