C# 9.0 で導入された init アクセサー(init-only setters)を使用すると、**オブジェクトの初期化時のみ値を設定可能で、その後は変更不可能(読み取り専用)**なプロパティを簡単に定義できます。
従来、不変なオブジェクトを作成するには「読み取り専用プロパティ(getのみ)」と「引数付きコンストラクタ」を組み合わせる必要がありましたが、init アクセサーを利用することで、柔軟なオブジェクト初期化子(new Class { Prop = val })の構文を維持したまま、安全性を高めることができます。
本記事では、書籍データを扱うクラスを例に、init アクセサーの実装方法と挙動を解説します。
実装例:初期化後は変更できない書籍クラス
以下のコードでは、Book クラスのプロパティに init を使用しています。これにより、インスタンス生成時には値をセットできますが、その後の代入操作はコンパイルエラーとなり、データの改変を防ぐことができます。
using System;
namespace InitAccessorExample
{
class Program
{
static void Main(string[] args)
{
// 1. オブジェクト初期化子での設定(許可される)
// initアクセサーを持つプロパティは、このタイミングであれば値を設定可能
var book = new Book
{
Title = "源氏物語",
Author = "紫式部"
};
Console.WriteLine($"登録完了: {book.Title}(著者: {book.Author})");
// 2. 初期化後の変更(許可されない)
// コンパイルエラー: init専用プロパティまたは読み取り専用プロパティには割り当てできません
// book.Author = "清少納言";
// 参考: 通常の代入を試みると、エディタ上で赤線(エラー)が表示され、
// 誤ってデータを書き換えるリスクを排除できる。
}
}
public class Book
{
// set の代わりに init を使用する
public string Title { get; init; }
public string Author { get; init; }
// 参考: 従来の記述(set)だと、いつでも変更可能になってしまう
// public string Description { get; set; }
}
}
解説
1. initアクセサーの役割
プロパティ定義で set の代わりに init キーワードを使用します。
public string Author { get; init; }
これにより、このプロパティは以下のタイミングでのみ値を設定できるようになります。
- コンストラクタ内
- オブジェクト初期化子(
new Book { ... }) with式を使用した複製時
2. コンストラクタボイラープレートの削減
以前の方法(getterのみ + コンストラクタ)では、プロパティが増えるたびにコンストラクタの引数も修正する必要があり、コードが肥大化しがちでした。 init アクセサーを使えば、引数なしコンストラクタ(デフォルトコンストラクタ)のままで、呼び出し側が必要なプロパティだけを選んで初期化できるため、コードがスッキリします。
まとめ
init アクセサーは、データの堅牢性(Immutability)と初期化の柔軟性を両立させる優れた機能です。
- 用途: マスタデータや設定値など、作成後に変更されるべきではないデータモデル。
- メリット: 意図しない値の書き換えをコンパイル時に防止できる。
「一度作ったら変えない」という性質を持つクラスには、積極的に set ではなく init を使用することをお勧めします。
