initキーワードとrequired修飾子を理解しよう!

[C#] initキーワードとrequired修飾子を理解しよう!

※ 当サイトは広告を含みます。

ここでは知っておくと便利なinitキーワードの紹介です。
ついでに一緒に利用するrequired修飾子も説明します。

initキーワード

英語的にinitとは初期化の省略形です。本来の単語はInitializeまたはInitializationになります。
よくあるプログラミング用語で、仮にメソッド名がInitialize()だと、初期化用のメソッドを意味します。

でっ、今回の話はC#に用意されたinitキーワードの話です。
説明には以下のクラスを利用することにします。


namespace Sample
{
  public class Test
  {
    public int X { get; set; }
  }

  internal class Program
  {
    static void Main(string[] args)
    {
      var obj = new Test()
      {
        X = 10,
      };

      Console.WriteLine(obj.X);
    }
  }
}

このプロパティXを、インスタンス生成時のみ値を代入できる設計にしたいと思います。
つまり、Xは読み取り専用であり、後に値の変更はできないように作り変えます。


namespace Sample
{
  public class Test
  {
    // 外部から変更不可能
    public int X { get; private set; } = 10;
  }

  internal class Program
  {
    static void Main(string[] args)
    {
      var obj = new Test();

      Console.WriteLine(obj.X);
    }
  }
}

とりあえず、上記のように変更しました。

しかし、これでは外部から値の変更ができません。
なのでコンストラクタで引数を受け取るようにします。


namespace Sample
{
  public class Test
  {
    public int X { get; private set; }

    // 引数付きコンストラクタで初期化
    public Test(int x)
    {
      this.X = x;
    }
  }

  internal class Program
  {
    static void Main(string[] args)
    {
      var obj = new Test(10);

      Console.WriteLine(obj.X);
    }
  }
}

普通はこうなります。でも、この方法だと無駄なコードが増えます。

仮に新しい読み取り専用プロパティで、外部から初回のみ代入が可能なYを作る場合、同じ仕組みで作ります。
その場合、コンストラクタの引数が増えます。さらに同様にZを作った場合、どんどん引数が増えます。

これではあまりに不便。そもそも利用しないプロパティまで引数を要求しては使い勝手も悪くなります。
可能ならインスタンス生成時にプロパティ経由で初期化する方法を利用したいです。

その願いを叶えてくれるのがinitキーワードです。
長くなりましたが、initキーワードで記述したベストアンサーがこれです。


namespace Sample
{
  public class Test
  {
    // setをinitに変更
    public int X { get; init; }
  }

  internal class Program
  {
    static void Main(string[] args)
    {
      var obj = new Test()
      {
        X = 10,
      };

      // 代入が不可能なのでビルドエラーになる
      //obj.X = 100;

      Console.WriteLine(obj.X);
    }
  }
}

このようにsetの代わりにinitと記述します。一緒にアクセス修飾子を記述すれば、アクセス範囲の制限も可能です。
そして、initキーワードを記述したプロパティはインスタンス生成時の代入でしか初期化できません。
仮に代入を試みると、コメントアウトで記述したようにビルドエラーになるため、確実に読み取り専用が保証されます。

required修飾子

先程のinitキーワードでは初期化をしないパターンを選択できます。
ほぼほぼ意味がありませんが、こんな感じのやつです。


namespace Sample
{
  public class Test
  {
    public int X { get; init; }
  }

  internal class Program
  {
    static void Main(string[] args)
    {
      var obj = new Test();

      Console.WriteLine(obj.X);
    }
  }
}

これが仕様上OKなら、それで良いです。例えばDefault値を持ち、初期化時のみ任意の値を代入できるとか。
required修飾子を利用するのは、そうではないパターンです。つまり、必ず値を代入して欲しい場合です。

このrequired修飾子をプロパティに付けることで、値の初期化を強制できます。
そして、required修飾子が付いたプロパティは、代入を行わない場合にビルドエラーになります。


namespace Sample
{
  public class Test
  {
    // requiredを付けると初期化が必須
    public required int X { get; init; }
  }

  internal class Program
  {
    static void Main(string[] args)
    {
      var obj = new Test()
      {
        // 代入しないとビルドエラー
        //X = 10,
      };

      Console.WriteLine(obj.X);
    }
  }
}

あとがき

実はこのキーワードは結構新しいです。確か.NET以降のC#でしか使えません。
例えば昔から継続開発してるシステムとか、そもそも記述できなかったりします。

管理人

業務だと.NET Frameworkがまだ現役。てか、サポート期間的に2030年くらいまで使えるはず。

りさ

長っ!。案外アップデートしないんですね。

管理人

客のために無償でバージョンを上げることにメリットがない。

◆ C#に関する学習コンテンツ

この記事は参考になりましたか?

👆このブログを支援する

関連記事

コメント

この記事へのコメントはありません。