Dwukrotne definiowanie właściwości w rekordzie

Dec 02 2020

W C # 9 można zdefiniować właściwość o tej samej nazwie w rekordzie zarówno w konstruktorze głównym, jak iw treści:

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

Ten kod kompiluje się bez błędów.

Podczas inicjowania wystąpienia takiego rekordu wartość przekazana konstruktorowi jest całkowicie ignorowana:

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

wydruki

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

Czy to zachowanie jest prawidłowe, czy jest to błąd? Jeśli jest poprawna, w jakich przypadkach jest przydatna?

Spodziewałem się, że kompilator albo odrzuci ten kod z błędem typu „Typ Catjuż zawiera definicję dla PawCount”, albo uzna właściwość w konstruktorze i w treści za taką samą, wykonując inicjalizację z konstruktora. Ten ostatni wariant może być przydatny do dostarczania właściwości z niestandardowym narzędziem pobierającym i / lub inicjalizatorem bez konieczności przepisywania wszystkich właściwości rekordu pozycyjnego w jego treści.

Faktyczne zachowanie nie ma dla mnie sensu.

Odpowiedzi

9 YairHalberstadt Dec 02 2020 at 22:10

Prawidłowy sposób to:

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

Jest to przydatne, ponieważ umożliwia np. Walidację

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

Specyfikacja tego jest tutaj: https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/records.md#members-of-a-record-type

Składowe są syntetyzowane, chyba że w treści rekordu zadeklarowano element członkowski z „zgodnym” podpisem lub dziedziczony jest dostępny konkretny niewirtualny element członkowski z „pasującym” podpisem. Dwóch członków jest uważanych za pasujących, jeśli mają ten sam podpis lub zostaliby uznani za „ukrywających się” w scenariuszu dziedziczenia.

Ponieważ więc istnieje już właściwość o takiej samej nazwie, jak parametr, kompilator nie zsyntetyzuje PawCountwłaściwości, więc po prostu dyskretnie ignoruje parametr, chyba że sam go użyjesz.