C# init と required

C#

C# 9.0 で導入された アクセサー init は、特定のプロパティについて、オブジェクトの初期化時にだけ設定可能にすることで、初期化後の変更をできないようにするものです。つまり、そのプロパティがイミュータブル(不変)であることを保証するものです。

C# 11 で導入されたキーワード required は、オブジェクトを作成する際に、特定のプロパティが初期化されていることを強制するものです。

この二つは関連が深いのでここでまとめて説明します。

init アクセサ

オブジェクトが生成された後、特定のプロパティの変更ができないようにして、そのプロパティをイミュータブル(不変)にします。

  • プロパティは、コンストラクタまたはオブジェクト初期化子の中でしか設定できません。
  • 一度設定されると、その後は読み取り専用となります。
  • 特にrecord 型では、プロパティは既定で init となっておりイミュータブルを保証します。
C#
public class Person
{
    public string Name { get; init; }
}

var person = new Person { Name = "Alice" }; 
person.Name = "Bob"; // コンパイルエラー!:このプロパティは変更できない

required キーワード

オブジェクト初期化子を使用してオブジェクトを作成する際に、特定のプロパティが必ず初期化されるようにコンパイラに強制します。C# 11、.NET 7.0 で導入されました。

  • required が付けられたプロパティは、オブジェクト初期化子を使用する際に、明示的に値を設定する必要があります。
  • 設定されていない場合、コンパイルエラーとなります。
  • コンストラクタで初期化される場合は required は適用されません。required はあくまで「オブジェクト初期化子を使う場合は必須」という意味です。
C#
public class Person
{
    public required string Name { get; init; }
}

var person = new Person { Name = "Alice" };
var person = new Person(); // コンパイルエラー!

この例では、6行目のように初期化子を使って Nameプロパティを設定することができますが、7行目のようにName プロパティを設定せずにオブジェクトを作成するとコンパイルエラーとなります。

また、次のコードのようにたとえ Name プロパティを初期化するコンストラクタを持っていたとしても、16行目のPerson オブジェクトの作成はコンパイルエラーとなります。

C#
   public class Person
   {
       public required string Name { get; init; }

       public Person(string name)
       {
           Name = name;
       }
   }

   internal class Program
   {
       static void Main(string[] args)
       {
           Console.WriteLine("Hello, World!");
           var pers = new Person("taro");  // コンパイルエラー!
       }
   }
   
// エラー (アクティブ) CS9035 必要なメンバー 'Person.Name' は、
// オブジェクト初期化子または属性コンストラクターに設定する必要があります。 

このエラーにおける属性コンストラクターとは、以下のような形式のコンストラクタでC# 12 で導入されました。

C#
public class Person(string name)
{
    public required string Name { get; init; } = name;
}

SetsRequiredMembers属性

[SetsRequiredMembers] 属性は、C# 11 で導入された required 修飾子と連携して使われる特殊な属性で、コンストラクターがすべての required メンバーを初期化していることをコンパイラーに伝えるためのものです。

C#
public class Person
{
    public required string FirstName { get; init; }
    public required string LastName  { get; init; }
    public int? Age                  { get; set; }

    // オブジェクト初期化子での使用
    Person p1 = new Person
    {
        FirstName = "Hanako",
        LastName  = "Suzuki",
        Age       = 28
    };

    // コンストラクター併用時
    [SetsRequiredMembers]
    public Person(string firstName, string lastName)
        => (FirstName, LastName) = (firstName, lastName);

    Person p2 = new Person("Ichiro", "Tanaka") { Age = 35 };
}

コメント

タイトルとURLをコピーしました