rksoftware

Visual Studio とか C# とかが好きです

C# 9.0 の確認「init 専用セッター」

C# 9.0 の確認の目次はこちら

■ init 専用セッター

ドキュメントはこちら

初期化時にだけ値をセットできる素敵なプロパティを作れます。もっとわかりやすく簡単に言うと、イミュータブルなオブジェクトが作れます。
初期化とは

  • コンストラクタ
  • 初期化子 ( new XXXX { プロパティ名 = 値} )
  • with 式 ( 型名 新しい変数 = 変数 with { プロパティ名 = 値 }; )

プロパティの宣言で普段 set と書いている所を init と書くと使えます。

public int MyProperty { get; init; }

■ init の確認

実際に init と書いてみましょう

// init の Property の場合
public record Saitama
{
    public string InitProperty { get; init; }
    public Saitama() => InitProperty = "value5";
    //public void SetValues() => InitProperty = "";       // これはエラー。初期化専用なので。うれしい
}

static void Main(string[] args)
{
    var saitama1 = new Saitama { InitProperty = "" };   // これはエラーにならない。初期化専用なので。うれしい
    var saitama2 = saitama1 with { InitProperty = "" }; // これはエラーにならない。初期化専用なので。うれしい
    //saitama2.InitProperty = "";                         // これはエラー。初期化専用なので。うれしい
}

プライベートなメソッド内でエラーになり変更不能になっていることがわかります。
そして初期化は可能で変更は不可です。素敵。これが init です。

■ 類似の手法

類似の手法を書いてみて init と違い不完全であることを確認していきましょう。

読み取り専用フィールド

// ReadOnly の Field の場合
public record Saitama
{
    public readonly string ReadOnlyField;
    public Saitama() => ReadOnlyField = "value1";
    //public void SetValues() => ReadOnlyField = "";          // これはエラー。ReadOnly なので。うれしい
}

static void Main(string[] args)
{
    //var saitama1 = new Saitama { ReadOnlyField = "" };      // これはエラー。ReadOnly なので。うれしくない
    //var saitama2 = saitama1 with { ReadOnlyField = "" };    // これはエラー。ReadOnly なので。うれしくない
    //saitama2.ReadOnlyField = "";                            // これはエラー。ReadOnly なので。うれしい
}

変更できないのはいいですが、初期化もできなくなってしまいます。こまります。

普通の変更可能なプロパティ

// set と get の Property の場合
public record Saitama
{
    public string SetGetProperty { get; set; }
    public Saitama() => SetGetProperty = "value2";
    public void SetValues() => SetGetProperty = "";         // これはエラーにならない。set できるので。うれしくない
}

static void Main(string[] args)
{
    var saitama1 = new Saitama { SetGetProperty = "" };     // これはエラーにならない。set できるので。うれしくない
    var saitama2 = saitama1 with { SetGetProperty = "" };   // これはエラーにならない。set できるので。うれしくない
    saitama2.SetGetProperty = "";                           // これはエラーにならない。set できるので。うれしくない
}

普通に変更できます。変更可能にしたので当たり前ですが。こまります。

読み取り専用プロパティ

// get のみの Property の場合
public record Saitama
{
    public string GetOnlyProperty { get; }
    public Saitama() => GetOnlyProperty = "value3";
    //public void SetValues() => GetOnlyProperty = "";        // これはエラー。set できないので。うれしい
}

static void Main(string[] args)
{
    //var saitama1 = new Saitama { GetOnlyProperty = "" };    // これはエラー。set できないので。うれしくない
    //var saitama2 = saitama1 with { GetOnlyProperty = "" };  // これはエラー。set できないので。うれしくない
    //saitama2.GetOnlyProperty = "";                          // これはエラー。set できないので。うれしい
}

変更できないのはいいですが、初期化もできなくなってしまいます。こまります。

セッターが private のプロパティ

public record Saitama
{
    public string PrivateSetProperty { get; private set; }
    public Saitama() => PrivateSetProperty = "value4";
    public void SetValues() => PrivateSetProperty = "";        // これはエラーにならない。private では set できるので。うれしくない
}

static void Main(string[] args)
{
    //var saitama1 = new Saitama { PrivateSetProperty = "" };    // これはエラー。set できないので。うれしくない
    //var saitama2 = saitama1 with { PrivateSetProperty = "" };  // これはエラー。set できないので。うれしくない
    //saitama2.PrivateSetProperty = "";                          // これはエラー。set できないので。うれしい
}

初期化できないし、メソッド内で変更できてしまっています。こまります。

■ まとめ

まあ、当たり前の結果になりました。
けれどもこの確認で確かに init 専用セッターでなければイミュータブルとして不完全であることが確認できました。
時代はイミュータブルです。積極的に使っていきましょう。