不変オブジェクトのパターン
作成後に決して変化しないオブジェクトを設計します。
「不変オブジェクトのパターン」はCoddyKit上の無料C# Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC# Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C# Academyコースには全4レッスンが含まれています。
不変オブジェクトを設計する
不変オブジェクトは構築後に変化しません。不変性によって、共有された変更可能な状態に関するさまざまなバグをなくし、オブジェクトを安全に受け渡したりキャッシュしたりできます。
get 専用プロパティ
最も単純な不変プロパティは get 専用で、コンストラクターから一度だけ設定します。
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 を使う
不変性と便利なオブジェクト初期化子構文を両立したい場合は、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値の等価性
不変オブジェクトでは、値に基づく等価性が求められることがよくあります。同じデータを持つ2つのインスタンスを等しいとみなす仕組みです。レコードでは標準で利用できますが、クラスではオーバーライドが必要です。
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); // prodreadonly フィールドと構造体
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簡単な確認
不変オブジェクトを更新する原則を確認してください。
まとめ
不変オブジェクトの設計パターンを学びました。
- 必須データには get 専用または
initプロパティとrequiredを使います。 - 変更は、新しいインスタンスを返すメソッドとしてモデル化します。
- 不変コレクション、または防御的にコピーしたコレクションを格納します。
- レコードは値の等価性と
withによるコピーを提供し、不変性によってスレッドセーフ性が得られます。
よくある質問
「不変オブジェクトのパターン」レッスンは無料ですか?
はい。「不変オブジェクトのパターン」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C# Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C# Academyコースには全4レッスンが含まれています。
「不変オブジェクトのパターン」で何を学びますか?
作成後に決して変化しないオブジェクトを設計します。 ブラウザで直接実行するハンズオンコードでC# Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
C# Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのC# Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「不変オブジェクトのパターン」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このC# Academyレッスンでコードを書いて実行できますか?
はい。すべてのC# Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- init専用setter
- required修飾子
- 不変オブジェクトのパターン
- レコードのwith式