レコード内のプロパティを2回定義する

Dec 02 2020

C#9では、プライマリコンストラクターと本体の両方のレコードに同じ名前のプロパティを定義できます。

record Cat(int PawCount)
{
    public int PawCount { get; init; }
}

このコードはエラーなしでコンパイルされます。

このようなレコードのインスタンスを初期化する場合、コンストラクターに提供される値は完全に無視されます。

Console.WriteLine(new Cat(4));
Console.WriteLine(new Cat(4) { PawCount = 1 });

プリント

Cat { PawCount = 0 }
Cat { PawCount = 1 }

この動作は正しいですか、それともバグですか?それが正しければ、それが役立つ場合は何ですか?

いずれかのようなエラーで、このコードを拒否するように、私は、コンパイラを期待「タイプは、Catすでにの定義が含まれているPawCountまたはコンストラクタから初期化を実行し、同じコンストラクタで、体内でプロパティを検討してください。後者のバリアントは、本体の位置レコードのすべてのプロパティを書き直すことなく、カスタムゲッターやイニシャライザーをプロパティに提供するのに役立ちます。

実際の動作は私には意味がありません。

回答

9 YairHalberstadt Dec 02 2020 at 22:10

これを行う正しい方法は次のとおりです。

record Cat(int PawCount)
{
    public int PawCount { get; init; } = PawCount;
}

これにより、検証などを実行できるので便利です。

record Cat(int PawCount)
{
    private int _pawCount;
    public int PawCount {
        get => _pawCount;
        init => _pawCount = value < 0 ? throw new ArgumentException() : value; 
    } = PawCount;
}

これの仕様はここにあります: https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/records.md#members-of-a-record-type

「一致する」署名を持つメンバーがレコード本体で宣言されていない限り、または「一致する」署名を持つアクセス可能な具象非仮想メンバーが継承されない限り、メンバーは合成されます。2つのメンバーが同じ署名を持っているか、継承シナリオで「隠れている」と見なされる場合、2つのメンバーは一致していると見なされます。

したがって、パラメーターと同じ名前のプロパティが既に存在するため、コンパイラーはPawCountプロパティを合成せず、明示的に使用しない限り、パラメーターを黙って無視します。