Двойное определение свойства в записи

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

Члены синтезируются, если в теле записи не объявлен член с «совпадающей» подписью или не наследуется доступный конкретный невиртуальный член с «совпадающей» сигнатурой. Два члена считаются совпадающими, если у них одна и та же подпись, или если они будут считаться «скрытыми» в сценарии наследования.

Итак, поскольку свойство с тем же именем, что и параметр, уже существует, компилятор не будет синтезировать PawCountсвойство, и поэтому просто игнорирует параметр, если вы сами не используете его явно.