Eine Eigenschaft in einem Datensatz zweimal definieren

Dec 02 2020

In C # 9 kann eine Eigenschaft mit demselben Namen in einem Datensatz sowohl in seinem primären Konstruktor als auch in seinem Hauptteil definiert werden:

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

Dieser Code wird fehlerfrei kompiliert.

Beim Initialisieren einer Instanz eines solchen Datensatzes wird der dem Konstruktor bereitgestellte Wert vollständig ignoriert:

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

druckt

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

Ist dieses Verhalten korrekt oder ist es ein Fehler? Wenn es richtig ist, in welchen Fällen ist es nützlich?

Ich habe erwartet, dass der Compiler diesen Code entweder mit einem Fehler wie "Der Typ Catenthält bereits eine Definition für PawCount" ablehnt oder die Eigenschaft im Konstruktor und im Body gleich betrachtet und ihre Initialisierung vom Konstruktor aus durchführt. Die letztere Variante kann nützlich sein, um die Eigenschaft mit einem benutzerdefinierten Getter und / oder Initialisierer zu versehen, ohne alle Eigenschaften des Positionsdatensatzes in seinem Körper neu schreiben zu müssen.

Das tatsächliche Verhalten macht für mich keinen Sinn.

Antworten

9 YairHalberstadt Dec 02 2020 at 22:10

Der richtige Weg, dies zu tun, ist:

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

Dies ist nützlich, da Sie damit z. B. eine Validierung durchführen können

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

Die Spezifikation hierfür ist hier: https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/records.md#members-of-a-record-type

Mitglieder werden synthetisiert, es sei denn, ein Mitglied mit einer "übereinstimmenden" Signatur wird im Datensatzkörper deklariert oder ein zugängliches konkretes nicht virtuelles Mitglied mit einer "übereinstimmenden" Signatur wird geerbt. Zwei Mitglieder gelten als übereinstimmend, wenn sie dieselbe Signatur haben oder in einem Vererbungsszenario als "versteckt" gelten.

Da also bereits eine Eigenschaft mit demselben Namen wie der Parameter vorhanden ist, synthetisiert der Compiler keine PawCountEigenschaft und ignoriert den Parameter nur stillschweigend, es sei denn, Sie verwenden ihn explizit selbst.