Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Defining a property in a record twice

Tags:

c#

c#-9.0

In C# 9, one can define a property with the same name in a record both in its primary constructor and in its body:

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

This code compiles without errors.

When initializing an instance of such a record, the value provided to the constructor is completely ignored:

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

prints

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

Is this behavior correct or is it a bug? If it’s correct, what are the cases in which it is useful?

I expected the compiler to either reject this code with an error like ‘The type Cat already contains a definition for PawCount or consider the property in the constructor and in the body the same, performing its initialization from the constructor. The latter variant could be useful to provide the property with a custom getter and/or initializer without having to rewrite all the properties of the positional record in its body.

The actual behavior makes no sense to me.

like image 782
user1067514 Avatar asked Dec 02 '20 15:12

user1067514


People also ask

How do you define a record in C#?

You define a record by declaring a type with the record keyword, instead of the class or struct keyword. Optionally, you can declare a record class to clarify that it's a reference type. A record is a reference type and follows value-based equality semantics.

Are records mutable?

A record struct declares a value type. Positional properties are immutable in a record class and a readonly record struct . They're mutable in a record struct .

When should I use C# records?

This is the stand out reason why you would want to use C# Records they are ideal in situations where you are going to need to compare objects and maybe you want to ensure the property values of an object cannot be changed during the execution of other processes.

Can records have Constructors C#?

A record type in C# 9 is a lightweight, immutable data type (or a lightweight class) that has read-only properties only. Because a record type is immutable, it is thread-safe and cannot mutate or change after it has been created. You can initialize a record type only inside a constructor.

How do I declare a property in a record type?

Record types are reference types, so a record instance contains only a reference to the data. You can use positional parameters to declare properties of a record and to initialize the property values when you create an instance: When you use the positional syntax for property definition, the compiler creates:

How do I create a record type with immutable properties?

Beginning with C# 9, you use the recordkeyword to define a reference typethat provides built-in functionality for encapsulating data. You can create record types with immutable properties by using positional parameters or standard property syntax: public record Person(string FirstName, string LastName);

Can a record inherit from another record?

A record can inherit from another record. However, a record can't inherit from a class, and a class can't inherit from a record. Positional parameters in derived record types

Can a record type be mutable?

While records can be mutable, they're primarily intended for supporting immutable data models. The record type offers the following features: Concise syntax for creating a reference type with immutable properties Built-in behavior useful for a data-centric reference type:


1 Answers

The correct way to do this is:

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

This is useful as it allows you to do e.g. validation

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

The spec for this is here: https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/records.md#members-of-a-record-type

Members are synthesized unless a member with a "matching" signature is declared in the record body or an accessible concrete non-virtual member with a "matching" signature is inherited. Two members are considered matching if they have the same signature or would be considered "hiding" in an inheritance scenario.

So since a property with the same name as the parameter already exists, the compiler wont synthesize a PawCount property, and so just silently ignores the parameter unless you use it yourself explicitly.

like image 141
Yair Halberstadt Avatar answered Oct 30 '22 02:10

Yair Halberstadt