0Pricing
C# Academy · 课时

不可变对象模式

设计创建后永不改变的对象。

不可变对象模式 是 CoddyKit 上的免费 C# Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C# Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C# Academy 课程共包含 4 节课。

设计不可变对象

不可变对象在构造完成后永远不会改变。不可变性可以消除围绕共享可变状态的一整类错误,并使对象能够安全地传递和缓存。

只读属性

最简单的不可变属性是只读属性,由构造函数赋值一次。

using System;

class Point {
    public int X { get; }
    public int Y { get; }
    public Point(int x, int y) { X = x; Y = y; }
}

var p = new Point(3, 4);
Console.WriteLine(p.X + "," + p.Y); // 3,4

使用初始化语法

当您希望保持不可变性,同时使用便捷的对象初始化器语法时,请使用 init 属性。

using System;

class Color {
    public int R { get; init; }
    public int G { get; init; }
    public int B { get; init; }
}

var c = new Color { R = 255, G = 128, B = 0 };
Console.WriteLine(c.R + "," + c.G + "," + c.B); // 255,128,0

使用 required 表示必需字段

将 required 与 init 结合使用,可以确保必需字段不会被遗漏,也不会被更改。

using System;

class Money {
    public required decimal Amount { get; init; }
    public required string Currency { get; init; }
}

var m = new Money { Amount = 9.99m, Currency = "USD" };
Console.WriteLine(m.Amount + " " + m.Currency); // 9.99 USD

返回新对象而不是修改对象

不可变类型会将“更改”操作设计为返回新实例的方法,从而保持原对象不变。

using System;

class Point {
    public int X { get; init; }
    public int Y { get; init; }
    public Point Move(int dx, int dy) => new Point { X = X + dx, Y = Y + dy };
}

var a = new Point { X = 1, Y = 1 };
var b = a.Move(2, 3);
Console.WriteLine(a.X + "," + a.Y + " -> " + b.X + "," + b.Y); // 1,1 -> 3,4

不可变集合

对于集合字段,请存储不可变集合或只读集合,这样就无法通过对象修改其中的内容。

using System;
using System.Collections.Immutable;

class Team {
    public ImmutableList<string> Members { get; init; } = ImmutableList<string>.Empty;
}

var t = new Team { Members = ["Ada", "Lin"] };
var t2 = new Team { Members = t.Members.Add("Sam") };
Console.WriteLine(t.Members.Count + " " + t2.Members.Count); // 2 3

防御性复制

如果必须接收可变集合,请在传入时复制它,这样外部更改就不会渗透到对象中。

using System;
using System.Collections.Generic;
using System.Linq;

class Snapshot {
    private readonly int[] data;
    public Snapshot(IEnumerable<int> source) => data = source.ToArray();
    public IReadOnlyList<int> Data => data;
}

var list = new List<int> { 1, 2, 3 };
var snap = new Snapshot(list);
list.Add(4); // does not affect snap
Console.WriteLine(snap.Data.Count); // 3

值相等性

不可变对象通常需要基于值的相等性:具有相同数据的两个实例会被视为相等。记录类型免费提供这一功能;类则需要重写相关成员。

using System;

record Coord(int X, int Y);

Console.WriteLine(new Coord(1, 2) == new Coord(1, 2)); // True

天然线程安全

由于不可变对象永远不会改变,因此可以在不使用锁的情况下安全地在线程之间共享。

using System;

class ReadonlyConfig {
    public required string Env { get; init; }
}

var cfg = new ReadonlyConfig { Env = "prod" };
// safe to read from many threads without synchronization
Console.WriteLine(cfg.Env); // prod

readonly 字段和结构体

使用 readonly 字段和 readonly struct,可以在字段级别为值类型强制实施不可变性。

using System;

readonly struct Vector {
    public readonly double X, Y;
    public Vector(double x, double y) { X = x; Y = y; }
    public double Length => Math.Sqrt(X * X + Y * Y);
}

Console.WriteLine(new Vector(3, 4).Length); // 5

综合运用

一个完全不可变的类型,结合了 required、init、值相等性和非变更式更新方法。

using System;

record Account(string Id, decimal Balance) {
    public Account Deposit(decimal amount) => this with { Balance = Balance + amount };
}

var a = new Account("A1", 100m);
var b = a.Deposit(50m);
Console.WriteLine(a.Balance + " -> " + b.Balance); // 100 -> 150

快速检查

请确认不可变更新原则。

回顾

您已经了解了不可变对象的设计模式。

  • 使用只读或 init 属性,并使用 required 表示必需数据。
  • 将更改变现为返回新实例的方法。
  • 存储不可变集合或经过防御性复制的集合。
  • 记录类型提供值相等性和 with 复制;不可变性则带来线程安全。

常见问题解答

「不可变对象模式」课时是免费的吗?

是的 — 「不可变对象模式」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C# Academy 课程的其余内容,请升级到 CoddyKit PRO。 C# Academy 课程共包含 4 节课。

「不可变对象模式」这节课中我会学到什么?

设计创建后永不改变的对象。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 C# Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 C# Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「不可变对象模式」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 C# Academy 课中编写并运行代码吗?

能。每节 C# Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 仅限 init 的 setter
  2. required 修饰符
  3. 不可变对象模式
  4. 记录类型中的 with 表达式
← 返回 C# Academy