C# 9.0 新機能 「init アクセサ （init 専用セッター）」


今更ながらC#9.0から追加された`init`アクセサ（`init` 専用セッター）を使ってみたのでメモ
https://docs.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-9#init-only-setters

# 前提

以下をメンバに持つ`Sample`というクラスがあったとします。

* `get`アクセサと`set`アクセサを持つ通常のプロパティ（`Property`）
* `get`アクセサしか持たない get-only（read-only） なプロパティ（`GetOnlyProperty`）

```cs
public class Sample
{
    public string Property { get; set; }    // get アクセサと set アクセサ
    public string GetOnlyProperty { get; }  // get アクセサのみ

    public Sample() { }

    public Sample(string property, string getOnlyProperty)
    {
        Property = property;
        GetOnlyProperty = getOnlyProperty;
    }
}
```

# C# 8.0 まで

各プロパティに値を設定したい場合、以下のように
`set`アクセサを持つプロパティ（`Property`）はコンストラクタでもオブジェクト初期化子でも、それ以外でも値を設定することができます。

一方で、`set`アクセサを持たない get-only なプロパティ（`GetOnlyProperty`）は、コンストラクタでしか値を設定することができません。
もちろん、自クラスのメソッド（`SampleMethod`）内でも設定することはできません。

```cs
public class Sample
{
    public string Property { get; set; }
    public string GetOnlyProperty { get; }

    public Sample() { }

    public Sample(string property, string getOnlyProperty)
    {
        Property = property;
        GetOnlyProperty = getOnlyProperty;
    }

    public void SampleMethod(string property, string getOnlyProperty)
    {
        Property = property;                //OK
        GetOnlyProperty = getOnlyProperty;  //Error
    }
}

public class Program
{
    private static void Main(string[] args)
    {
        //コンストラクタで値を設定
        var s1 = new Sample("foo", "bar"); //OK

        //値を設定
        s1.Property = "baz";		//OK
        s1.GetOnlyProperty = "baz";	//Error

        //オブジェクト初期化子で値を設定
        var s2 = new Sample
        {
            Property = "foo",			//OK
            GetOnlyProperty = "bar",	//Error
        };
    }
}
```

コンストラクタでの初期化以降、値を書き換えられることが無い get-only なプロパティ（`GetOnlyProperty`）は、安全に利用することができますが、少々制限が厳しすぎると感じていた方もいらっしゃるのではないでしょうか。

具体的には、**オブジェクト初期化子でも値を設定することができない**という点です。

C# 8.0 までは 初期化処理（コンストラクタ及びオブジェクト初期化子）では値が設定できるが
それ以外は値を設定することができないプロパティを定義することはできません。


# C# 9.0

C# 9.0 で導入された`init`アクセサ を利用すると、
上述した通り、C# 8.0 までは実現することができなかった
初期化処理（コンストラクタ及びオブジェクト初期化子）では値が設定できるが
それ以外は値を設定することができないプロパティを定義することができます。

`get-only`なプロパティに対して`init-only`なプロパティと呼びます。

定義方法は非常に簡単で、`set`の代わりに`init`と記述するだけです。

```cs
public string InitOnlyProperty { get; init; }
```

get-only なプロパティとの主な違いは
init-only プロパティでは、オブジェクト初期化子で値を設定することが出来るという点です。

もちろん get-only なプロパティ同様に、コンストラクタでも値を設定することができますし、
それ以外の場所では値を設定することができません。
(他の`init`アクセサ内は除く)

`init`アクセサを利用することによって、 get-only なプロパティよりも柔軟に利用できるプロパティが定義できるようになりました。

```cs
public class Sample
{
    public string Property { get; set; }
    public string GetOnlyProperty { get; }
    public string InitOnlyProperty { get; init; }

    public Sample() { }

    public Sample(string property, string getOnlyProperty, string initOnlyProperty)
    {
        Property = property;                   //OK
        GetOnlyProperty = getOnlyProperty;     //OK
        InitOnlyProperty = initOnlyProperty;   //OK
    }

    public void SampleMethod(string property, string getOnlyProperty, string initOnlyProperty)
    {
        Property = property;                    //OK
        GetOnlyProperty = getOnlyProperty;      //Error
        InitOnlyProperty = initOnlyProperty;    //Error
    }
}

public class Program
{
    private static void Main(string[] args)
    {
        //コンストラクタで値を設定
        var s1 = new Sample("foo", "bar", "baz");

        //値を設定
        s1.Property = "qux";            //OK
        s1.GetOnlyProperty = "qux";     //Error
        s1.InitOnlyProperty = "qux";    //Error

        //オブジェクト初期化子で値を設定
        // get-only プロパティでは値を設定できないが、 init-only プロパティでは可能
        var s2 = new Sample
        {
            Property = "foo",           //OK
            GetOnlyProperty = "bar",    //Error
            InitOnlyProperty = "baz",       //OK
        };
    }
}
```

以上となります。