核心区别

值类型 引用类型
关键字 struct enum int ... class record(默认) interface string ...
存哪里 通常 (变量是栈上的指针)
赋值 复制 复制引用
默认值 0 / false / null字段 null
可继承 不可

例子

struct Point { public int X, Y; }
class Person { public string Name = ""; }

var p1 = new Point { X = 1, Y = 2 };
var p2 = p1;             // 复制
p2.X = 99;
Console.WriteLine(p1.X); // 1(p1 没变)

var a = new Person { Name = "Alice" };
var b = a;               // 复制引用
b.Name = "Bob";
Console.WriteLine(a.Name); // Bob(同一个对象)

内置类型分类

// 值类型
int   long  short  byte  sbyte  uint  ulong  ushort
float double decimal
bool  char
struct enum

// 引用类型
class object string interface delegate
array (T[])

注意 string引用类型但表现像值类型——不可变,==/Equals 比较内容(特殊优化)。

装箱 / 拆箱

int n = 42;
object o = n;        // 装箱:值类型 → 堆上的 object
int m = (int)o;      // 拆箱:必须显式

装箱有性能代价——分配堆内存 + GC 压力。热路径里避免:

// ❌ 隐式装箱
ArrayList list = new ArrayList();   // 老 API,存 object
list.Add(42);   // int → object,装箱

// ✅ 泛型避免
List<int> list2 = new List<int>();
list2.Add(42);  // 不装箱

所有泛型集合 List<T> Dictionary<K,V> 都不装箱——这是 C# 2.0 引入泛型最大的好处之一。

struct 的合理使用

什么时候用 struct?

  • 数据小(一般 ≤ 16 字节)
  • 不可变或近乎不可变
  • 语义上是"值"(坐标、颜色、向量、Money)
// ✅ 适合
readonly struct Vec2
{
    public readonly double X, Y;
    public Vec2(double x, double y) { X = x; Y = y; }
    public Vec2 Add(Vec2 other) => new(X + other.X, Y + other.Y);
}

// ❌ 不适合:大、可变、有继承感
struct Order  // 几十个字段
{
    public List<Item> Items;   // 每次复制都共享引用,可变 list 在多个值副本里 → 混乱
    ...
}

默认用 class,确认是值语义再切 struct。

record(C# 9)

record class 是带"值语义"的引用类型——== 比较所有字段相等:

record class Point(int X, int Y);

var p1 = new Point(1, 2);
var p2 = new Point(1, 2);
Console.WriteLine(p1 == p2);    // True(字段相等)
Console.WriteLine(ReferenceEquals(p1, p2));   // False(不同对象)

record struct(C# 10)—— 类型 + 自动 == / GetHashCode:

record struct Point(int X, int Y);

详见 第 12 篇

引用变量 vs 实际对象

Person? p = null;        // 引用变量 p 指向 null
p = new Person();        // 创建对象,p 指向它
p = null;                // 解除引用,对象等 GC

// 多个引用指同一对象
Person p1 = new Person { Name = "Alice" };
Person p2 = p1;
p2.Name = "Bob";
// p1.Name 也是 "Bob"

ref / out / in(按引用传值类型)

static void Inc(int x) { x++; }      // 复制 x,外面没变

static void IncRef(ref int x) { x++; }   // 按引用传

int n = 5;
IncRef(ref n);
Console.WriteLine(n);    // 6

out:调用前不要求初始化,方法内必须赋值:

static bool TryDivide(int a, int b, out int result)
{
    if (b == 0) { result = 0; return false; }
    result = a / b;
    return true;
}

if (TryDivide(10, 3, out var r)) Console.WriteLine(r);

in:只读引用(避免大 struct 复制):

static double Distance(in Vec2 a, in Vec2 b)
{
    return Math.Sqrt((a.X-b.X)*(a.X-b.X) + (a.Y-b.Y)*(a.Y-b.Y));
}

比较

值类型 引用类型 说明
== 默认比值 默认比引用 record 比所有字段
Equals(other) 比值(要正确重写) 默认比引用 string、record 重写过
ReferenceEquals(a, b) 永远比引用 永远比引用 引用相等

心智模型

  • 基本类型 + 小值:值类型,传递不担心
  • 业务对象 / 大数据:class 引用类型
  • 不可变的数据载体record class(最现代选择)
  • 算法 + 性能敏感readonly struct / Span<T> / ref

→ 下一篇 字符串