Skip to content

17a-结构体(Struct) ​

结构体是值类型,适合表示轻量级数据对象。struct 不支持继承,但可以实现接口。


一、定义与基本使用 ​

csharp
// 基本定义
public struct Point
{
    public int X;
    public int Y;
}

// 使用
Point p1;
p1.X = 10;
p1.Y = 20;
Console.WriteLine($"({p1.X}, {p1.Y})");  // (10, 20)

// 构造函数初始化
Point p2 = new Point(30, 40);

带构造函数和属性的 struct ​

csharp
public struct Rectangle
{
    public int Width { get; }
    public int Height { get; }

    public Rectangle(int width, int height)
    {
        Width = width;
        Height = height;
    }

    public int Area => Width * Height;

    public override string ToString() => $"{Width}×{Height}";
}

// 使用
var rect = new Rectangle(10, 20);
Console.WriteLine(rect.Area);  // 200

二、值类型行为 ​

赋值复制 ​

csharp
struct Point { public int X; public int Y; }

Point a = new Point { X = 1, Y = 2 };
Point b = a;        // 复制整个数据
b.X = 999;          // 修改 b 不影响 a
Console.WriteLine(a.X);  // 1(a 是独立的)
Console.WriteLine(b.X);  // 999

作为参数传递 ​

csharp
struct Point { public int X; public int Y; }

void ModifyPoint(Point p)
{
    p.X = 100;  // 修改的是副本,不影响原变量
}

var pt = new Point { X = 1, Y = 2 };
ModifyPoint(pt);
Console.WriteLine(pt.X);  // 1(未被修改)

// 使用 ref 传递引用(避免复制)
void ModifyPointRef(ref Point p)
{
    p.X = 100;  // 修改原变量
}
ModifyPointRef(ref pt);
Console.WriteLine(pt.X);  // 100

集合中修改 struct 的注意事项 ​

csharp
struct Point { public int X; public int Y; }

// ⚠️ 数组可以直接修改(因为数组元素是变量)
Point[] points = new Point[3];
points[0].X = 10;  // ✅ 直接修改

// ⚠️ List 中的 struct 不能直接修改(需要取出副本再写回)
List<Point> list = new List<Point>();
list.Add(new Point { X = 1, Y = 2 });
// list[0].X = 10;  // ❌ 编译错误:不能修改临时表达式

// 正确做法:取出 → 修改 → 写回
Point temp = list[0];
temp.X = 10;
list[0] = temp;

三、Struct vs Class 详细对比 ​

对比项Struct(结构体)Class(类)
类型值类型引用类型
存储位置栈(Stack)或内嵌在父对象中堆(Heap)
默认值所有字段置零null
赋值行为复制全部数据复制引用(指向同一对象)
继承不支持继承,可实现接口支持继承和多态
构造函数必须有参数的无参构造(C# 10+ 支持显式无参构造)默认有无参构造,可重载
析构函数不支持支持
null 赋值不可直接赋 null(需 int? 或 Nullable<T>)可为 null
作为参数传值(方法内修改不影响原变量)传引用(方法内修改影响原对象)
this不可在成员方法中重新赋值 this可在成员方法中重新赋值 this
性能小数据(≤16 字节)时高效需要 GC 管理,额外内存开销
适用场景轻量级数据对象复杂业务对象

选择指南 ​

条件选 struct选 class
数据量小(≤16 字节)✅
数据量大(>16 字节)✅
需要继承/多态✅
需要引用语义(共享)✅
需要可变性✅
不可变的值语义✅
短生命周期(局部变量)✅
需要长期存储/共享✅
频繁创建大量实例✅

四、特殊 Struct 类型 ​

1. readonly struct(C# 7.2+) ​

csharp
// 保证不可变性,编译器可做更多优化
public readonly struct ReadOnlyPoint
{
    public int X { get; }
    public int Y { get; }

    public ReadOnlyPoint(int x, int y)
    {
        X = x;
        Y = y;
    }

    // readonly struct 中所有成员都是只读的
    // 不能有 setter
}

// 使用
var p = new ReadOnlyPoint(10, 20);
// p.X = 30;  // ❌ 编译错误

2. ref struct(C# 7.2+) ​

csharp
// ref struct 只能分配在栈上,不能装箱,不能作为类成员
ref struct SpanWrapper
{
    public Span<int> Data { get; }

    public SpanWrapper(Span<int> data)
    {
        Data = data;
    }
}

// 限制:
// - 不能装箱(不能转为 object、interface)
// - 不能作为 class 的字段
// - 不能用于 async 方法(可能跨越 await 边界)
// - 不能用于迭代器(yield return)
// - 可用于高性能场景(Span<T>, ReadOnlySpan<T>)

3. 带 readonly 成员的 struct ​

csharp
public struct PointWithReadonly
{
    public int X { get; set; }
    public int Y { get; set; }

    // readonly 成员保证不修改结构体状态
    public readonly double DistanceFromOrigin()
        => Math.Sqrt(X * X + Y * Y);

    // readonly 属性
    public readonly string Display => $"({X}, {Y})";
}

五、Struct 实现接口 ​

csharp
public interface IMovable
{
    void Move(int dx, int dy);
}

public struct Point : IMovable
{
    public int X;
    public int Y;

    public void Move(int dx, int dy)
    {
        X += dx;
        Y += dy;
    }

    public override string ToString() => $"({X}, {Y})";
}

// 使用
// 直接调用(不装箱)
Point p = new Point { X = 1, Y = 2 };
p.Move(10, 20);
Console.WriteLine(p);  // (11, 22)

// 通过接口调用(装箱!有性能开销)
IMovable movable = p;  // 装箱
movable.Move(5, 5);    // 修改的是堆上的副本

注意: struct 赋值给接口变量时会发生装箱。如果性能敏感,尽量避免。


六、性能考虑 ​

什么时候 struct 更快 ​

csharp
// ✅ struct 更快的场景:大量小对象的短期使用
struct Point { public float X; public float Y; }

// 创建 1000 万个点的数组
Point[] points = new Point[10_000_000];
// 直接存储值,内存连续,缓存友好

// ❌ class 对比:每个对象单独分配,GC 压力大
class PointClass { public float X; public float Y; }
PointClass[] points2 = new PointClass[10_000_000];
for (int i = 0; i < points2.Length; i++)
    points2[i] = new PointClass();  // 1000 万次堆分配

什么时候 struct 更慢 ​

csharp
// ❌ struct 更慢的场景:大数据结构频繁复制
struct BigData
{
    public long A, B, C, D, E, F, G, H;  // 64 字节
}

BigData a = new BigData();
BigData b = a;  // 复制 64 字节(比引用复制 8 字节慢得多)

// ✅ 这种情况下用 class 更好
class BigDataClass { /* 同字段 */ }
BigDataClass c = new BigDataClass();
BigDataClass d = c;  // 只复制 8 字节的引用

七、综合案例:颜色值 ​

csharp
// 使用 struct 表示一个 RGB 颜色
public readonly struct Color
{
    public byte R { get; }
    public byte G { get; }
    public byte B { get; }
    public byte A { get; }

    public Color(byte r, byte g, byte b, byte a = 255)
    {
        R = r; G = g; B = b; A = a;
    }

    // 预定义颜色(静态属性)
    public static Color Red => new Color(255, 0, 0);
    public static Color Green => new Color(0, 255, 0);
    public static Color Blue => new Color(0, 0, 255);
    public static Color White => new Color(255, 255, 255);
    public static Color Black => new Color(0, 0, 0);

    // 亮度
    public readonly double Luminance => 0.299 * R + 0.587 * G + 0.114 * B;

    // 混合
    public Color Blend(Color other, double ratio = 0.5)
    {
        double r = R * (1 - ratio) + other.R * ratio;
        double g = G * (1 - ratio) + other.G * ratio;
        double b = B * (1 - ratio) + other.B * ratio;
        return new Color((byte)r, (byte)g, (byte)b, A);
    }

    public override string ToString() => $"#{R:X2}{G:X2}{B:X2}";
}

// 使用
var c1 = Color.Red;
var c2 = Color.Blue;
var blended = c1.Blend(c2, 0.5);
Console.WriteLine(blended);  // 混合色

核心知识点总结 ​

Struct 关键特性 ​

特性说明
值类型继承自 System.ValueType
复制语义赋值、传参时复制整个数据
无继承不能作为基类或被继承,可实现接口
无析构函数生命周期由作用域决定
栈分配小数据时高性能,减少 GC 压力
sealed 隐式所有 struct 隐式 sealed

注意事项 ​

  1. struct 应小于 16 字节——过大时复制成本高,不如用 class
  2. 赋值即复制——修改 struct 副本不影响原变量
  3. 不可变性推荐——readonly struct 避免意外复制
  4. List 中的 struct 不能直接修改字段——需要取出 → 修改 → 写回
  5. 装箱发生在接口赋值时——IMovable m = myStruct; 会装箱
  6. 无参构造需 C# 10+——旧版本必须通过参数赋值初始化所有字段
  7. 适合值语义的类型——坐标、颜色、复数、货币等
  8. 不适合多态场景——需要继承体系时应使用 class

Released under the MIT License.