Definir uma propriedade em um registro duas vezes

Dec 02 2020

No C # 9, pode-se definir uma propriedade com o mesmo nome em um registro, tanto em seu construtor primário quanto em seu corpo:

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

Este código é compilado sem erros.

Ao inicializar uma instância de tal registro, o valor fornecido ao construtor é completamente ignorado:

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

estampas

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

Este comportamento está correto ou é um bug? Se estiver correto, quais são os casos em que é útil?

Eu esperava que o compilador rejeitasse esse código com um erro como 'O tipo Catjá contém uma definição para PawCount' ou considerasse a propriedade no construtor e no corpo da mesma, executando sua inicialização a partir do construtor. A última variante pode ser útil para fornecer à propriedade um getter e / ou inicializador personalizado sem ter que reescrever todas as propriedades do registro posicional em seu corpo.

O comportamento real não faz sentido para mim.

Respostas

9 YairHalberstadt Dec 02 2020 at 22:10

A maneira correta de fazer isso é:

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

Isso é útil porque permite que você faça, por exemplo, validação

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

A especificação para isso está aqui: https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/records.md#members-of-a-record-type

Os membros são sintetizados, a menos que um membro com uma assinatura "correspondente" seja declarado no corpo do registro ou um membro não virtual concreto acessível com uma assinatura "correspondente" seja herdado. Dois membros são considerados correspondentes se tiverem a mesma assinatura ou seriam considerados "ocultos" em um cenário de herança.

Portanto, como já existe uma propriedade com o mesmo nome do parâmetro, o compilador não sintetiza uma PawCountpropriedade e, portanto, ignora silenciosamente o parâmetro, a menos que você o use explicitamente.