はじめに
本記事はinitキーワードを使うことでオブジェクト初期化子で生成可能なバリューオブジェクトを作る方法についてまとめます。
initキーワードを使わない場合
initキーワードを使わない場合、読み取り専用のプロパティを用意し、コンストラクタでプロパティの値を受け取り、初期化することでバリューオブジェクトを定義できます。
以下のような感じです。
/// <summary>
/// バリューオブジェクト
/// </summary>
internal class ValueObject
{
#region プロパティ
/// <summary>
/// 値
/// </summary>
internal string Value { get; } = string.Empty;
#endregion
#region コンストラクタ
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="value">値</param>
internal ValueObject(string value)
{
Value = value;
}
#endregion
}
バリューオブジェクトを生成するには以下のようにコンストラクタに値を渡してオブジェクトを生成することになります。
// バリューオブジェクトをコンストラクタ経由で値を渡して生成
var valueObject = new ValueObject("value");
バリューオブジェクトにするためにプロパティは読み取り専用としているため、仮に上記のValueObjectクラスに引数なしコンストラクタがあったとしても、以下のようにオブジェクト初期化子を使っての生成はできません。
// プロパティが読み取り専用なので、以下はビルドエラーになる
var valueObject = new ValueObject(){ Value = "value" };
initキーワードを使う場合
オブジェクト初期化子を使って生成できるバリューオブジェクトを定義するためには、以下のようにinitキーワードを使って初期化時のみ値を設定可能なプロパティを定義します。
以下のような感じです。
/// <summary>
/// バリューオブジェクト
/// </summary>
internal class ValueObject
{
#region プロパティ
/// <summary>
/// 値
/// </summary>
internal string Value { get; init; } = string.Empty;
#endregion
}
バリューオブジェクトを生成するには以下のようにオブジェクト初期化子で値を設定してオブジェクトを生成することになります。
// バリューオブジェクトをオブジェクト初期子で作成
var valueObject = new ValueObject(){ Value = "value" };
initキーワードを使ってプロパティを定義しているので、オブジェクト生成後に以下のようにプロパティの値を上書きしようとするとビルドエラーとなります。
したがって、オブジェクト生成以降プロパティの値が変更できず、バリューオブジェクトとして扱うことができます。
// 初期化以外での値の設定はできないので、以下はビルドエラーとなる
valueObject.Value = "changedValue";
まとめ
本記事はinitキーワードを使うことでオブジェクト初期化子で生成可能なバリューオブジェクトを作る方法をまとめました。
ただし、今回紹介した方法では、オブジェクト初期化子でプロパティの初期化を強制することができません。
C#11.0で追加されるrequired修飾子を使うことで、オブジェクト初期化子でのプロパティの初期化を強制できるようになるため、これを使うことで問題点を解消できると思います。
C#11.0が正式リリースされた後に問題点を解消した例をまた記事にできたらなと思っています。