C#のrequiredとは?必須プロパティの使い方・エラー原因・initとの違いをわかりやすく解説
はじめに
C#でクラスやDTOを作っていると、次のような悩みによく出会います。
C#public class User
{
public string Name { get; set; }
public string Email { get; set; }
}
このコードでは、NameやEmailは本来必須の値であっても、次のように何も設定せずにインスタンスを作れてしまいます。
C#var user = new User();
nullable参照型を有効にしていれば警告は出ますが、「このプロパティはオブジェクト作成時に必ず設定してほしい」という意図を、より明確に表現したい場面があります。
そこで使えるのが、C# 11で追加されたrequiredです。requiredを使うと、プロパティやフィールドを「オブジェクト作成時に必ず初期化すべきメンバー」として宣言できます。MicrosoftのC#リファレンスでも、requiredはフィールドまたはプロパティに対して、オブジェクト初期化子などで初期化される必要があることを示す修飾子として説明されています。
この記事では、C#のrequiredとは何か、基本的な使い方、よくあるエラー、initとの違い、コンストラクタとの関係、実務での注意点までわかりやすく解説します。
1. C#のrequiredとは?必須プロパティを強制できる機能
1-1. requiredの意味と役割
C#のrequiredは、プロパティやフィールドに対して「このメンバーはインスタンス作成時に必ず設定してください」とコンパイラへ伝えるための修飾子です。
代表的な使い方は次のとおりです。
C#public class User
{
public required string Name { get; init; }
public required string Email { get; init; }
}
このように書くと、Userを作成する側はNameとEmailを設定しなければなりません。
C#var user = new User
{
Name = "Taro",
Email = "taro@example.com"
};
requiredの目的は、実行時のバリデーションではなく、コンパイル時に初期化漏れを見つけやすくすることです。つまり、requiredは「必須入力チェック」そのものではなく、「オブジェクト作成時の初期化契約」を表す機能だと考えると理解しやすくなります。
1-2. requiredで解決できる「プロパティの初期化漏れ」
従来のC#では、必須のプロパティがあっても、引数なしコンストラクタやオブジェクト初期化子を使うと値の設定漏れが起こりやすい問題がありました。
C#public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
}
var product = new Product
{
Price = 1000
};
この例では、Nameが未設定のままです。nullable参照型を有効にしていれば警告は出る可能性がありますが、プロパティを「作成時に必ず設定する」という意図はコード上で十分に表現されていません。
requiredを付けると、次のように初期化漏れをコンパイルエラーとして検出できます。
C#public class Product
{
public required string Name { get; init; }
public required decimal Price { get; init; }
}
var product = new Product
{
Price = 1000
}; // エラー: Nameが設定されていない
このように、C# requiredはDTO、設定クラス、APIリクエストモデルなど、「オブジェクト作成時に必要な値がそろっていること」を明確にしたい場面で役立ちます。
1-3. requiredが使えるC#のバージョン
required修飾子はC# 11以降で利用できます。Microsoftのリファレンスでも、requiredはC# 11から利用可能な修飾子として説明されています。
一般的には、.NET 7以降のプロジェクトではC# 11を使いやすくなっています。ただし、実際に使えるC#のバージョンはプロジェクトのLangVersionやターゲットフレームワークの設定に依存します。
たとえば、古いプロジェクトでrequiredを書いてエラーになる場合は、.csprojに次のような設定を追加することで解決できることがあります。
XML<PropertyGroup>
<LangVersion>11.0</LangVersion>
</PropertyGroup>
または、最新の言語機能を使うために次のように指定することもあります。
XML<PropertyGroup>
<LangVersion>latest</LangVersion>
</PropertyGroup>
ただし、チーム開発では安易にlatestにするより、利用するC#バージョンを明示した方がビルド環境の差異を減らせます。
1-4. requiredを使うとコンパイル時に何がチェックされるのか
requiredを付けると、コンパイラは「その型のインスタンスを作成する式で、すべてのrequiredメンバーが設定されているか」をチェックします。Microsoftのドキュメントでも、型の新しいインスタンスを初期化する式では、すべてのrequiredメンバーを初期化する必要があると説明されています。
たとえば次のコードはOKです。
C#var user = new User
{
Name = "Taro",
Email = "taro@example.com"
};
一方で、次のコードはEmailが未設定なのでエラーになります。
C#var user = new User
{
Name = "Taro"
};
注意点として、requiredは「値が妥当か」までは検証しません。たとえば空文字やnullが代入されても、構文上は「設定した」と見なされることがあります。
C#var user = new User
{
Name = null,
Email = "taro@example.com"
};
Nameが非nullableのstringであればnullable警告の対象になりますが、required自体がnullを完全に禁止するわけではありません。Microsoftのプロパティ解説でも、requiredと非nullableを混同しないよう注意されており、requiredプロパティへnullやdefaultを設定すること自体は可能だと説明されています。
2. C# requiredの基本的な使い方
2-1. requiredプロパティの書き方
requiredプロパティは、プロパティ宣言の前にrequiredを付けて書きます。
C#public class Customer
{
public required string Name { get; set; }
}
よく使われるのは、initと組み合わせた次の形です。
C#public class Customer
{
public required string Name { get; init; }
}
setを使うと作成後も値を変更できますが、initを使うとオブジェクト初期化時のみ設定できます。そのため、「必ず設定してほしいが、作成後は変更されたくない」というプロパティではrequired initがよく使われます。
C#public class Customer
{
public required string Name { get; init; }
public required string Email { get; init; }
}
この書き方は、読みやすく、オブジェクトの初期化ルールも明確です。
2-2. オブジェクト初期化子でrequiredプロパティを設定する方法
requiredを付けたプロパティは、基本的にオブジェクト初期化子で設定します。
C#var customer = new Customer
{
Name = "Yamada",
Email = "yamada@example.com"
};
オブジェクト初期化子とは、new Customer { ... }の中でプロパティを設定する書き方です。requiredはこの書き方と相性がよく、どの値が必須なのかをインスタンス作成時に明示できます。
たとえばAPIレスポンス用のDTOでは次のように書けます。
C#public class CustomerResponse
{
public required int Id { get; init; }
public required string Name { get; init; }
public required string Email { get; init; }
}
var response = new CustomerResponse
{
Id = 1,
Name = "Yamada",
Email = "yamada@example.com"
};
必須プロパティを忘れるとコンパイル時に検出されるため、単純な設定漏れを防ぎやすくなります。
2-3. requiredを付けたプロパティを設定しない場合のエラー例
次のクラスを考えます。
C#public class User
{
public required string Name { get; init; }
public required string Email { get; init; }
}
このとき、次のコードはエラーになります。
C#var user = new User
{
Name = "Taro"
};
Emailが設定されていないためです。典型的には次のようなエラーが表示されます。
CS9035: Required member 'User.Email' must be set in the object initializer or attribute constructor.
修正するには、すべてのrequiredメンバーを設定します。
C#var user = new User
{
Name = "Taro",
Email = "taro@example.com"
};
また、何も設定せずに作成する次のコードもエラーになります。
C#var user = new User();
このように、requiredは「作成した後で設定する予定だった」というミスを防ぐのに有効です。
2-4. class・struct・recordでrequiredを使う例
requiredはclassだけでなく、structやrecordにも使えます。Microsoftのリファレンスでも、struct、class、record、record structで宣言されたフィールドやプロパティにrequiredを適用できると説明されています。
classでの例です。
C#public class User
{
public required string Name { get; init; }
}
structでの例です。
C#public struct Point
{
public required int X { get; init; }
public required int Y { get; init; }
}
var point = new Point
{
X = 10,
Y = 20
};
recordでの例です。
C#public record UserDto
{
public required string Name { get; init; }
public required string Email { get; init; }
}
record structでも使えます。
C#public readonly record struct Money
{
public required decimal Amount { get; init; }
public required string Currency { get; init; }
}
ただし、recordの主コンストラクタの引数そのものにrequiredを付けることはできません。必要な場合は、明示的にプロパティを宣言します。
C#public record UserDto
{
public required string Name { get; init; }
}
2-5. requiredを複数のプロパティに指定する場合の書き方
複数のプロパティを必須にしたい場合は、それぞれにrequiredを付けます。
C#public class OrderRequest
{
public required int CustomerId { get; init; }
public required string ProductCode { get; init; }
public required int Quantity { get; init; }
public string? Note { get; init; }
}
この場合、CustomerId、ProductCode、Quantityは必須で、Noteは任意です。
C#var request = new OrderRequest
{
CustomerId = 10,
ProductCode = "BOOK-001",
Quantity = 2
};
requiredが付いているプロパティと付いていないプロパティを分けることで、「何が必須で何が任意か」がクラス定義から読み取りやすくなります。
3. requiredで発生しやすいエラー原因と解決策
3-1. CS9035:Required member must be setの原因
requiredで最もよく見るエラーがCS9035です。
CS9035: Required member 'User.Name' must be set in the object initializer or attribute constructor.
このエラーは、requiredメンバーが設定されていない状態でインスタンスを作成したときに発生します。Microsoftのコンパイラエラー一覧でも、CS9035はrequiredメンバーをオブジェクト初期化子または属性コンストラクタで設定する必要があるエラーとして説明されています。
例を見てみましょう。
C#public class User
{
public required string Name { get; init; }
}
var user = new User(); // CS9035
解決策は、requiredプロパティを初期化することです。
C#var user = new User
{
Name = "Taro"
};
3-2. requiredプロパティを初期化していないケース
次のように一部のプロパティだけを設定した場合もエラーになります。
C#public class User
{
public required string Name { get; init; }
public required string Email { get; init; }
}
var user = new User
{
Name = "Taro"
}; // Emailが未設定
修正は単純です。
C#var user = new User
{
Name = "Taro",
Email = "taro@example.com"
};
特にDTOや設定クラスでは、後から必須プロパティを追加したときに既存コードでCS9035が大量に出ることがあります。これは不便に見えますが、「新しく追加された必須項目を設定していない箇所」をコンパイラが教えてくれているとも言えます。
3-3. コンストラクタで値を設定しているのにエラーになる理由
次のコードを見てください。
C#public class User
{
public required string Name { get; init; }
public User(string name)
{
Name = name;
}
}
var user = new User("Taro");
一見すると、コンストラクタでNameを設定しているので問題なさそうです。しかし、このコードではrequiredメンバーが満たされたとコンパイラが判断できず、エラーになることがあります。
理由は、requiredのチェックは主に「オブジェクト初期化子でrequiredメンバーが設定されたか」を見る仕組みだからです。コンストラクタ内部で本当にすべてのrequiredメンバーを設定しているかを、コンパイラが通常の形では保証しません。
この場合は、次のようにSetsRequiredMembers属性を使います。
C#using System.Diagnostics.CodeAnalysis;
public class User
{
public required string Name { get; init; }
[SetsRequiredMembers]
public User(string name)
{
Name = name;
}
}
var user = new User("Taro");
3-4. SetsRequiredMembersAttributeでエラーを解消する方法
SetsRequiredMembersAttributeは、「このコンストラクタはrequiredメンバーをすべて設定します」とコンパイラに伝えるための属性です。
C#using System.Diagnostics.CodeAnalysis;
public class User
{
public required string Name { get; init; }
public required string Email { get; init; }
[SetsRequiredMembers]
public User(string name, string email)
{
Name = name;
Email = email;
}
}
これにより、次のような生成が可能になります。
C#var user = new User("Taro", "taro@example.com");
ただし、重要な注意点があります。SetsRequiredMembersは「本当にすべて設定しているか」をコンパイラが検証してくれる属性ではありません。Microsoftのドキュメントでも、この属性はコンストラクタがすべてのrequiredメンバーを初期化することをコンパイラへ表明するものであり、コンパイラが実際に初期化しているかを検証するわけではないと説明されています。
たとえば次のコードは危険です。
C#using System.Diagnostics.CodeAnalysis;
public class User
{
public required string Name { get; init; }
public required string Email { get; init; }
[SetsRequiredMembers]
public User(string name)
{
Name = name;
// Emailを設定していない
}
}
このように、SetsRequiredMembersは便利ですが、使う側に責任があります。必要な値を本当にすべて設定しているか、レビューやテストで確認しましょう。
3-5. CS8618との違いとnullable警告への対応
requiredと一緒に理解しておきたいのが、nullable参照型の警告CS8618です。
CS8618: Non-nullable property 'Name' must contain a non-null value when exiting constructor.
CS8618は、非nullable参照型のプロパティがコンストラクタ終了時点で初期化されていない場合に出る警告です。
C#public class User
{
public string Name { get; set; } // CS8618が出ることがある
}
これに対して、requiredを使うと次のように書けます。
C#public class User
{
public required string Name { get; init; }
}
これにより、「このプロパティはインスタンス作成時に設定される」という意図をコンパイラに伝えられます。EF Coreのドキュメントでも、C# 11以降ではrequiredメンバーが、非nullableプロパティの初期化問題に対する有効な解決策として紹介されています。
ただし、requiredはnullable警告をすべて解決する万能機能ではありません。required stringにnullを入れた場合は、別途nullable警告が出る可能性があります。
C#var user = new User
{
Name = null
};
つまり、requiredは「設定されたか」を扱い、nullable参照型は「nullでよいか」を扱います。両者は役割が異なります。
3-6. required stringなのにnullを代入できてしまうケース
required stringと書くと、「絶対にnullにならない」と思うかもしれません。しかし、これは誤解です。
C#public class User
{
public required string Name { get; init; }
}
var user = new User
{
Name = null
};
nullable参照型が有効であれば警告は出ますが、required自体は「null禁止」を意味しません。requiredが見るのは、あくまで「プロパティに何かが代入されたか」です。
nullをより厳密に防ぎたい場合は、次のような対策を組み合わせます。
C#public class User
{
private string _name = string.Empty;
public required string Name
{
get => _name;
init => _name = string.IsNullOrWhiteSpace(value)
? throw new ArgumentException("Name is required.")
: value;
}
}
または、コンストラクタで検証する方法もあります。
C#public class User
{
public string Name { get; }
public User(string name)
{
Name = string.IsNullOrWhiteSpace(name)
? throw new ArgumentException("Name is required.")
: name;
}
}
nullや空文字を実行時に拒否したいなら、requiredだけでなく、バリデーション、コンストラクタ、ドメインルールを組み合わせる必要があります。
4. requiredとinitの違い
4-1. initは「作成後に変更できない」ことを表す
initは、プロパティをオブジェクト初期化時だけ設定できるようにするアクセサです。
C#public class User
{
public string Name { get; init; }
}
この場合、次のように作成時には設定できます。
C#var user = new User
{
Name = "Taro"
};
しかし、作成後の変更はできません。
C#user.Name = "Jiro"; // エラー
Microsoftのプロパティ解説でも、initアクセサはオブジェクト構築時のみ新しい値を代入するために使われると説明されています。
つまり、initの主な役割は「作成後の変更を防ぐこと」です。
4-2. requiredは「作成時に必ず設定する」ことを表す
一方、requiredは「作成時に必ず設定すること」を表します。
C#public class User
{
public required string Name { get; set; }
}
この場合、作成時にはNameを設定する必要があります。
C#var user = new User
{
Name = "Taro"
};
しかし、setを使っているので作成後も変更できます。
C#user.Name = "Jiro"; // OK
つまり、requiredは「必須かどうか」を表し、initは「いつまで変更できるか」を表します。
4-3. required initを組み合わせるメリット
requiredとinitは組み合わせて使うことが多いです。
C#public class User
{
public required string Name { get; init; }
public required string Email { get; init; }
}
この書き方には、次のようなメリットがあります。
requiredによって、作成時に必ず値を設定させることができます。
C#var user = new User
{
Name = "Taro",
Email = "taro@example.com"
};
initによって、作成後の変更を防げます。
C#user.Email = "new@example.com"; // エラー
そのため、DTO、設定値、読み取り専用に近いデータモデルでは、required initが非常に使いやすい書き方です。
4-4. required setとの違い
required setの例を見てみましょう。
C#public class User
{
public required string Name { get; set; }
}
この場合、作成時にはNameを設定する必要があります。
C#var user = new User
{
Name = "Taro"
};
ただし、作成後も変更できます。
C#user.Name = "Jiro"; // OK
一方、required initでは作成後に変更できません。
C#public class User
{
public required string Name { get; init; }
}
var user = new User
{
Name = "Taro"
};
user.Name = "Jiro"; // エラー
使い分けはシンプルです。作成後も値を変更したいならrequired set、作成時だけ設定して以後は変更したくないならrequired initを使います。
4-5. readonly・private setとの使い分け
readonly、private set、init、requiredは似て見えますが、役割が違います。
private setは、外部からの変更を禁止し、クラス内部からのみ変更できるようにします。
C#public class User
{
public string Name { get; private set; }
public void ChangeName(string name)
{
Name = name;
}
}
initは、オブジェクト初期化時には外部から設定できますが、作成後は変更できません。
C#public class User
{
public string Name { get; init; }
}
readonlyフィールドは、コンストラクタまたは宣言時にのみ代入できるフィールドです。
C#public class User
{
private readonly string _name;
public User(string name)
{
_name = name;
}
}
requiredは、値を必ず設定させるための修飾子です。
C#public class User
{
public required string Name { get; init; }
}
実務では、単純なDTOならrequired init、内部状態を制御したいドメインモデルならコンストラクタやprivate setを使うと整理しやすくなります。
5. requiredとコンストラクタの関係
5-1. requiredはコンストラクタ引数の代わりになるのか
requiredは、場合によってはコンストラクタ引数の代わりのように使えます。
C#public class User
{
public required string Name { get; init; }
public required string Email { get; init; }
}
この場合、次のように作成できます。
C#var user = new User
{
Name = "Taro",
Email = "taro@example.com"
};
コンストラクタを使うなら次のようになります。
C#public class User
{
public string Name { get; }
public string Email { get; }
public User(string name, string email)
{
Name = name;
Email = email;
}
}
どちらも「必須の値を渡す」という目的は達成できます。ただし、意味は少し異なります。
requiredはオブジェクト初期化子を使いやすくし、プロパティ名を明示して設定できます。一方、コンストラクタは不正な値の検証や生成ルールの強制に向いています。
5-2. コンストラクタ初期化とオブジェクト初期化子の違い
コンストラクタ初期化は、引数の順番で値を渡します。
C#var user = new User("Taro", "taro@example.com");
短く書けますが、引数が増えると意味がわかりにくくなることがあります。
C#var product = new Product("A001", "Book", 1000, true, 10);
一方、オブジェクト初期化子はプロパティ名を明示できます。
C#var product = new Product
{
Code = "A001",
Name = "Book",
Price = 1000,
IsActive = true,
Stock = 10
};
値の意味が読み取りやすい反面、生成時の複雑な検証や不変条件の保証には向かない場合があります。
5-3. コンストラクタでrequiredメンバーを満たす方法
コンストラクタでrequiredメンバーを満たしたい場合は、SetsRequiredMembersを使います。
C#using System.Diagnostics.CodeAnalysis;
public class User
{
public required string Name { get; init; }
public required string Email { get; init; }
[SetsRequiredMembers]
public User(string name, string email)
{
Name = name;
Email = email;
}
}
これにより、次のようにコンストラクタだけで作成できます。
C#var user = new User("Taro", "taro@example.com");
もちろん、オブジェクト初期化子を使うコンストラクタも残せます。
C#public User()
{
}
この場合は、次のように作成します。
C#var user = new User
{
Name = "Taro",
Email = "taro@example.com"
};
5-4. SetsRequiredMembersを使う際の注意点
SetsRequiredMembersを使うと、コンパイラのrequiredチェックを「このコンストラクタでは満たしている」と見なさせることができます。しかし、属性を付けたからといって、コンパイラがコンストラクタ本体を詳細に検証してくれるわけではありません。Microsoftのドキュメントでも、SetsRequiredMembersはコンパイラのチェックを無効化する側面があるため注意して使う必要があるとされています。
悪い例です。
C#using System.Diagnostics.CodeAnalysis;
public class User
{
public required string Name { get; init; }
public required string Email { get; init; }
[SetsRequiredMembers]
public User(string name)
{
Name = name;
}
}
このコードではEmailを設定していません。それでもSetsRequiredMembersにより、呼び出し側ではrequiredチェックを回避できてしまいます。
そのため、SetsRequiredMembersを使う場合は、次の点を確認しましょう。
すべてのrequiredメンバーを本当に設定しているか
派生クラスや基底クラスのrequiredメンバーも考慮しているか
コンストラクタチェーンで属性の付け忘れがないか
テストで初期化結果を確認しているか
特にライブラリや共通基盤コードでは、安易に付けない方が安全です。
5-5. requiredとデフォルトコンストラクタの扱い
requiredメンバーを持つクラスでも、引数なしコンストラクタは定義できます。
C#public class User
{
public User()
{
}
public required string Name { get; init; }
}
ただし、呼び出し側はrequiredメンバーを設定する必要があります。
C#var user = new User
{
Name = "Taro"
};
次のように何も設定しない場合はエラーです。
C#var user = new User(); // エラー
フレームワークによっては、デシリアライズやORMの都合で引数なしコンストラクタが必要になることがあります。そのような場合でも、requiredを使えば、通常のアプリケーションコードでは初期化漏れを防ぎやすくなります。
ただし、リフレクションや一部のフレームワーク経由で生成される場合は、C#コンパイラによるチェックが働かないケースがあります。実務では「requiredがあるから必ず安全」と考えず、利用するフレームワークの挙動を確認することが重要です。
6. requiredを使うべき場面・使わない方がよい場面
6-1. DTOや設定クラスでrequiredが向いている理由
requiredが特に向いているのは、DTOや設定クラスです。
C#public class MailSettings
{
public required string Host { get; init; }
public required int Port { get; init; }
public required string UserName { get; init; }
public required string Password { get; init; }
}
設定クラスでは、必要な値が不足しているとアプリケーションの起動時や処理中に問題が起こります。requiredを付けておけば、コード上で手動生成する場合の設定漏れを防ぎやすくなります。
DTOでも、レスポンスやリクエストの必須項目が明確になります。
C#public class CreateUserRequest
{
public required string Name { get; init; }
public required string Email { get; init; }
}
このようなクラスは、主にデータを運ぶ目的であり、複雑な振る舞いを持たないことが多いため、required initとの相性が良いです。
6-2. ドメインモデルでrequiredを使う場合の注意点
ドメインモデルでrequiredを使う場合は注意が必要です。
たとえば、ユーザー名やメールアドレスに厳密なルールがある場合、requiredだけでは不十分です。
C#public class User
{
public required string Name { get; init; }
public required string Email { get; init; }
}
このコードでは、空文字や不正なメール形式を防げません。
C#var user = new User
{
Name = "",
Email = "invalid"
};
ドメインモデルでは、オブジェクトが常に正しい状態であることが重要です。そのため、コンストラクタやファクトリメソッドで検証する設計の方が適していることがあります。
C#public class User
{
public string Name { get; }
public string Email { get; }
public User(string name, string email)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("Name is required.", nameof(name));
}
if (string.IsNullOrWhiteSpace(email))
{
throw new ArgumentException("Email is required.", nameof(email));
}
Name = name;
Email = email;
}
}
requiredは便利ですが、ドメインの不変条件を守る仕組みとしては弱い場合があります。
6-3. バリデーション目的だけでrequiredを使うべきではない理由
requiredはバリデーション機能ではありません。
次のようなコードでも、Nameには値が「設定」されています。
C#var user = new User
{
Name = "",
Email = "taro@example.com"
};
空文字を禁止したい場合、requiredだけでは防げません。最小文字数、最大文字数、形式チェック、範囲チェックなどもrequiredの対象外です。
バリデーションを行うなら、次のような仕組みを使います。
C#public class CreateUserRequest
{
public required string Name { get; init; }
public required string Email { get; init; }
public void Validate()
{
if (string.IsNullOrWhiteSpace(Name))
{
throw new InvalidOperationException("Name is required.");
}
if (!Email.Contains('@'))
{
throw new InvalidOperationException("Email is invalid.");
}
}
}
ASP.NET CoreならDataAnnotations、FluentValidation、独自バリデーションなどを組み合わせることもあります。
requiredは「設定漏れ対策」、バリデーションは「値の妥当性チェック」と分けて考えるのが大切です。
6-4. nullable参照型と組み合わせる場合の考え方
requiredはnullable参照型と組み合わせることで、より意図を明確にできます。
C#public class User
{
public required string Name { get; init; }
public string? Nickname { get; init; }
}
この例では、Nameは必須でnullにしたくない値、Nicknameは任意でnullでもよい値です。
required stringは「作成時に必ず設定してほしい非nullableな値」を表します。
C#public required string Name { get; init; }
required string?は「作成時に必ず設定してほしいが、値としてnullは許可する」という意味になります。
C#public required string? MiddleName { get; init; }
このような設計は、たとえば「項目自体は必ず指定するが、値はnullの可能性がある」というAPI仕様を表したい場合に使えます。
ただし、required string?は読み手にとって意図がわかりにくいこともあります。必要な場合はコメントやドキュメントで補足するとよいでしょう。
6-5. requiredよりコンストラクタの方が適しているケース
次のようなケースでは、requiredよりコンストラクタの方が適しています。
まず、生成時に厳密な検証が必要な場合です。
C#public class Money
{
public decimal Amount { get; }
public string Currency { get; }
public Money(decimal amount, string currency)
{
if (amount < 0)
{
throw new ArgumentOutOfRangeException(nameof(amount));
}
if (string.IsNullOrWhiteSpace(currency))
{
throw new ArgumentException("Currency is required.", nameof(currency));
}
Amount = amount;
Currency = currency;
}
}
次に、複数の値の組み合わせにルールがある場合です。
C#public class Period
{
public DateTime Start { get; }
public DateTime End { get; }
public Period(DateTime start, DateTime end)
{
if (start > end)
{
throw new ArgumentException("Start must be before End.");
}
Start = start;
End = end;
}
}
このようなクラスでは、プロパティごとにrequiredを付けるだけでは不正な状態を防げません。オブジェクトの整合性を守る必要があるなら、コンストラクタやファクトリメソッドを使う方が安全です。
7. requiredの実務上の注意点
7-1. System.Text.Jsonのデシリアライズ時の挙動
System.Text.Jsonでは、JSONデシリアライズ時の必須プロパティとしてrequiredを扱えます。Microsoftのドキュメントでは、JSONペイロードに必ず存在すべきプロパティを示す方法として、C#のrequired修飾子、JsonRequiredAttribute、JsonPropertyInfo.IsRequiredの3つが紹介されています。
たとえば次のクラスがあります。
C#public class Person
{
public required string Name { get; set; }
public int Age { get; set; }
}
JSONにNameが存在しない場合、デシリアライズ時に例外が発生します。
C#var json = """{"Age": 42}""";
var person = JsonSerializer.Deserialize<Person>(json);
// JsonException
ここで重要なのは、requiredはC#コード上の初期化チェックだけでなく、System.Text.Jsonではデシリアライズ時の必須メタデータとしても扱われる点です。Microsoftのドキュメントでも、シリアライザーの観点ではC#のrequiredと[JsonRequired]は同じメタデータに対応すると説明されています。
ただし、ソース生成モードやC#以外の言語、またはJSONだけで必須扱いしたい場合は、[JsonRequired]の方が適していることがあります。
7-2. Entity Framework Coreで使う場合の注意点
EF Coreでも、requiredは非nullableプロパティの初期化問題を扱ううえで役立ちます。EF Coreのドキュメントでは、C# 11以降ではrequiredメンバーにより、エンティティ作成時に非nullableプロパティを初期化することをコンパイラが保証できると説明されています。
例です。
C#public class Customer
{
public int Id { get; set; }
public required string Name { get; set; }
}
このように書くと、通常のコードでCustomerを作成する際にNameの設定漏れを防げます。
C#var customer = new Customer
{
Name = "Yamada"
};
ただし、EF Coreではナビゲーションプロパティ、遅延読み込み、マテリアライズ、コンストラクタバインディングなども関係します。すべてのプロパティに機械的にrequiredを付けるのではなく、EF Coreがどのようにエンティティを生成するかを理解して使う必要があります。
特に既存プロジェクトでnullable参照型を有効にすると、以前は任意扱いだった参照型プロパティが必須扱いになり、マイグレーションでデータベース列のnull許容が変更される可能性があります。EF Coreのドキュメントでも、既存プロジェクトでnullable参照型を有効化する際は注意が必要だと説明されています。
7-3. リフレクションではrequiredが強制されないケース
requiredのチェックは主にC#コンパイラによるものです。そのため、リフレクションや一部のシリアライザー、ORM、DIコンテナなどがインスタンスを生成する場合、通常のC#コードと同じチェックが働かないことがあります。
たとえば、次のようなクラスがあるとします。
C#public class User
{
public required string Name { get; init; }
}
通常のC#コードでは次のコードはエラーです。
C#var user = new User();
しかし、リフレクションでインスタンスを作る場合、コンパイル時のオブジェクト初期化チェックを通らないことがあります。
C#var user = Activator.CreateInstance<User>();
このような生成方法では、requiredだけに依存すると未初期化のオブジェクトができる可能性があります。フレームワーク経由で生成される型では、実行時検証や初期化後チェックも検討しましょう。
7-4. 継承クラスでrequiredプロパティを扱う方法
継承でrequiredを使う場合、基底クラスのrequiredメンバーにも注意が必要です。
C#public class Person
{
public required string Name { get; init; }
}
public class Employee : Person
{
public required string EmployeeNumber { get; init; }
}
この場合、Employeeを作成するときは、PersonのNameとEmployeeのEmployeeNumberの両方を設定する必要があります。
C#var employee = new Employee
{
Name = "Taro",
EmployeeNumber = "E001"
};
また、requiredメンバーを派生クラスで隠すことはできません。Microsoftのリファレンスでも、派生クラスは基底クラスで宣言されたrequiredメンバーを隠せず、requiredプロパティをオーバーライドする場合は派生側でもrequiredを含める必要があると説明されています。
C#public class BaseUser
{
public virtual required string Name { get; init; }
}
public class AdminUser : BaseUser
{
public override required string Name { get; init; }
}
継承階層が深い場合、どのプロパティが必須なのか見えにくくなることがあります。requiredを多用する設計では、継承よりもコンポジションを使った方がシンプルになる場合もあります。
7-5. ライブラリ公開時にrequiredがAPI契約へ与える影響
公開ライブラリでrequiredを使う場合は、API契約への影響を意識する必要があります。
たとえば、既存の公開クラスに後からrequiredプロパティを追加すると、そのクラスを利用している側のコードでコンパイルエラーが発生する可能性があります。
C#public class LibraryOptions
{
public required string ApiKey { get; init; }
}
利用側は次のように修正しなければなりません。
C#var options = new LibraryOptions
{
ApiKey = "xxxxx"
};
これは、ライブラリ利用者にとっては破壊的変更になり得ます。特にNuGetパッケージなど外部公開しているAPIでは、既存クラスにrequiredを追加する前に、メジャーバージョンアップや移行手順の案内を検討しましょう。
また、requiredメンバーを持つ型は、new()制約があるジェネリック型引数として使えない制約があります。Microsoftのリファレンスでも、requiredメンバーを持つ型は、型パラメータにnew()制約がある場合の型引数として使えないと説明されています。
C#public void Create<T>() where T : new()
{
var instance = new T();
}
このようなAPIと組み合わせる可能性がある型では、requiredの導入に注意が必要です。
8. requiredに関するよくある質問
8-1. requiredを付ければnullを完全に防げる?
防げません。
requiredは「そのメンバーが初期化されたか」をチェックする機能であり、「nullではないこと」を完全に保証する機能ではありません。
C#public class User
{
public required string Name { get; init; }
}
var user = new User
{
Name = null
};
nullable参照型が有効なら警告は出ますが、requiredだけでnullを完全に禁止できるわけではありません。
nullを防ぎたい場合は、次のような対策を組み合わせます。
C#public class User
{
private string _name = string.Empty;
public required string Name
{
get => _name;
init => _name = value ?? throw new ArgumentNullException(nameof(value));
}
}
また、空文字や不正値も防ぎたい場合は、追加のバリデーションが必要です。
8-2. requiredはプロパティだけでなくフィールドにも使える?
使えます。
C#public class User
{
public required string Name;
}
ただし、実務ではpublicフィールドよりもプロパティを使うことが一般的です。
C#public class User
{
public required string Name { get; init; }
}
プロパティなら、後からバリデーションやアクセス制御を追加しやすくなります。そのため、特別な理由がなければrequiredはプロパティに付けるのがおすすめです。
8-3. requiredとDataAnnotationsのRequired属性は何が違う?
C#のrequiredとDataAnnotationsの[Required]は、名前は似ていますが役割が違います。
C#のrequiredはコンパイル時の初期化チェックです。
C#public class User
{
public required string Name { get; init; }
}
一方、DataAnnotationsの[Required]は、主に実行時バリデーションで使われます。
C#using System.ComponentModel.DataAnnotations;
public class UserRequest
{
[Required]
public string? Name { get; set; }
}
ASP.NET Coreのモデルバインディングやバリデーションでは、[Required]が入力値検証に使われます。C#のrequiredは、C#コード上でオブジェクトを作るときの設定漏れを検出するものです。
つまり、次のように使い分けます。
C#public class CreateUserRequest
{
[Required]
public required string Name { get; init; }
}
このように両方を使うこともあります。requiredは開発時の初期化漏れ対策、[Required]は実行時の入力検証と考えるとわかりやすいです。
8-4. requiredは後から既存クラスに追加してもよい?
追加自体はできますが、影響範囲に注意が必要です。
たとえば既存クラスにrequiredを追加します。
C#public class User
{
public required string Name { get; init; }
}
すると、既存の次のコードはエラーになります。
C#var user = new User();
社内アプリの閉じた範囲であれば、コンパイルエラーを手がかりに修正すればよいでしょう。しかし、外部に公開しているライブラリや、多数のプロジェクトから参照される共通クラスに追加すると、利用側のビルドを壊す可能性があります。
後からrequiredを追加する場合は、次のような対応を検討しましょう。
影響範囲を検索する
移行期間を設ける
既定値で済むなら初期値を設定する
破壊的変更としてバージョンを上げる
代替コンストラクタを用意する
既存クラスへのrequired追加は、単なる内部実装変更ではなく、利用側に影響するAPI変更として扱うのが安全です。
8-5. requiredを使うとパフォーマンスに影響はある?
通常のC#コードでrequiredを使うこと自体に、大きな実行時パフォーマンス影響はありません。
requiredの主な役割は、コンパイル時に初期化漏れをチェックすることです。実行時に毎回重い検証処理を行う機能ではありません。
C#public class User
{
public required string Name { get; init; }
}
ただし、System.Text.Jsonのデシリアライズ時など、フレームワークがrequiredメタデータを見て必須プロパティの存在を確認する場面では、そのフレームワーク側の処理が関係します。とはいえ、通常はパフォーマンスを理由にrequiredを避ける必要はほとんどありません。
むしろ、初期化漏れによるバグをコンパイル時に検出できるメリットの方が大きいでしょう。
まとめ
C#のrequiredは、プロパティやフィールドを「オブジェクト作成時に必ず設定すべきメンバー」として宣言できる機能です。
C#public class User
{
public required string Name { get; init; }
public required string Email { get; init; }
}
このように書くことで、次のような初期化漏れをコンパイル時に検出できます。
C#var user = new User
{
Name = "Taro"
}; // Emailが未設定なのでエラー
requiredを理解するうえで重要なのは、initやnullable参照型との違いです。requiredは「作成時に必ず設定する」、initは「作成後に変更できない」、nullable参照型は「nullを許容するかどうか」を表します。
実務では、DTO、設定クラス、APIモデルなどにはrequired initが向いています。一方で、複雑なドメインルールや値の妥当性を守りたい場合は、コンストラクタやファクトリメソッド、バリデーションを組み合わせる必要があります。
また、SetsRequiredMembers、System.Text.Json、EF Core、継承、ライブラリ公開時の破壊的変更など、実務上の注意点もあります。
C# requiredは、正しく使えば初期化漏れを減らし、コードの意図を明確にできる便利な機能です。ただし、「requiredを付ければnullや不正値を完全に防げる」と考えるのではなく、コンパイル時の初期化チェックを強化する仕組みとして使うのがポイントです。

