C# 9.0 から init アクセサーが追加されています。比較的に使いやすいアクセサーですが、利用しているでしょうか。仕様を把握しているでしょうか。
これまでの流れ
getter/setter で記述するパターンは、(アクセス修飾子を除くと)だいたいこんな感じの流れがありました。
一番の基本形となるパターン1で戸惑うことは無いけど、2と3の違いを把握できていますか。そこが init アクセサーを追加したポイントになると思います。先に違いの要点を整理すると下図のとおり:
変数 | コンストラクター | オブジェクト初期化子 | メソッド |
---|---|---|---|
1.get; set; |
〇 | 〇 | 〇 |
2.get; |
〇 | × | × |
3.get; init; |
〇 | 〇 | × |
パターンの1のように、メソッドからアクセスできてしまうと、値変更のアクセスに制限はありません。パターン2は、値変更がコンスタクター内でしかできなかったため、かなり厳しい制限です。つまり、get; init;
が必要になった理由は get;
のみだと値を初期化する制限がコーディングする上で厳しすぎるという雰囲気になり、ちょうどいい値の初期化制限を実現するために生まれたものです。
アクセス修飾子 (private, protected) は class 外からのアクセス制限をするものなので、目的が異なります。(コンストラクターを private にして、singleton パターンを実装することもあるので、初期化と関係が無いわけでもありませんが)
一番の違いはオブジェクト初期化子
オブジェクト初期子でのデータの書き換えは、下のような場合です。SharpLab でも書いてみました。
var data = new SampleData { Data = 100 };
record 型専用の初期化 with
C# 9.0 から with 式を利用できるようになりました。with 式は、一般的なクラスの値初期化で利用できるキーワードではなくて、データの読み書きに特化した record 型のときに利用できるキーワードです。(C# 10.0 以降は、構造体と匿名型も対象ですが、利用目的は基本同じ)
using System; public class Program { record Sample { public string Name { get; init; } } public static void Main() { var program = new Program(); program.Run(); } public void Run() { var sample1 = new Sample { Name = "ハルウララ" }; var sample2 = sample1 with { Name = "キングヘイロー" }; Console.WriteLine($"Data value = {sample1.Name}."); Console.WriteLine($"Data value = {sample2.Name}."); } }
Data value = ハルウララ. Data value = キングヘイロー.
その他に readonly フィールドの書き換えが
init
アクセサー内で可能です。ただ、ケースとして珍しいと思うので記述していません。
「++C++; - プロパティ」の解説では、以下のような意見があります。
歴史的経緯で
init
という新キーワードが使われていますが、もし C# をフルスクラッチで作り直せるならreadonly
が最初からinit
相当の仕様になっていたと思います。
そんなわけで、init を使ってみるのはそんなに怖いものではないです。