今更ながら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
)
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
)内でも設定することはできません。
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
と記述するだけです。
public string InitOnlyProperty { get; init; }
get-only なプロパティとの主な違いは
init-only プロパティでは、オブジェクト初期化子で値を設定することが出来るという点です。
もちろん get-only なプロパティ同様に、コンストラクタでも値を設定することができますし、
それ以外の場所では値を設定することができません。
(他のinit
アクセサ内は除く)
init
アクセサを利用することによって、 get-only なプロパティよりも柔軟に利用できるプロパティが定義できるようになりました。
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
};
}
}
以上となります。