24

I am going through C# 9 new features which will be released soon. Init-Only properties are being introduced with it.

The one big limitation today is that the properties have to be mutable for object initializers to work: They function by first calling the object’s constructor (the default, parameterless one in this case) and then assigning it to the property setters.

Init-only properties fix that! They introduce an init accessor that is a variant of the set accessor which can only be called during object initialization:

public class Person
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
}

With this declaration, the client code above is still legal, but any subsequent assignment to the FirstName and LastName properties is an error. What does this line mean? If ReadOnly also does the same thing then what is the use of an Init-Only property?

Vivek Nuna
  • 25,472
  • 25
  • 109
  • 197
  • 3
    @Supergibbs Its completely ok, It was really a good practical question. But some people are good at pulling others' legs. – Vivek Nuna Jun 17 '20 at 13:57

4 Answers4

21

As stated in the new C# 9 features post,

The one big limitation today is that the properties have to be mutable for object initializers to work: They function by first calling the object’s constructor (the default, parameterless one in this case) and then assigning to the property setters.

However, value types with readonly modifiers are immutable as stated in readonly documentation.

Therefore, it is not possible to use readonly properties with object initializers.

However, with Init-only properties you can use object initializers.

emre nevayeshirazi
  • 18,983
  • 12
  • 64
  • 81
  • Thank you, so other than this difference, both will be same in all aspects? Or in other words can I say to use object initializer to readonlty properties they have come up with Init_Only properties? – Vivek Nuna Jun 14 '20 at 12:52
  • I think this is the main reason for the implementation of this new feature but without more information about the implementation details, I am not sure. – emre nevayeshirazi Jun 14 '20 at 12:57
  • Can you provide example which shows the difference between the two – Vivek Nuna Jun 14 '20 at 13:06
  • 1
    @viveknuna Go and watch https://mybuild.microsoft.com/sessions/fc099bf7-8c85-4c3b-9a90-2d917342f945?source=schedule . – mjwills Jun 14 '20 at 13:31
  • With init, Callers can use property initializer syntax to set the values, while still preserving the immutability (readonly properties). – M.Hassan Mar 05 '22 at 15:12
  • FYI: "_types with readonly modifiers are immutable_" _Only_ as far as the C# compiler is concerned. On the IL/CLR level, readonly fields are mutable. Which actually makes them modifiable by C# source code as well, for example by mutating a readonly field through reflection... –  Dec 18 '22 at 16:09
14

The purpose of Init-only properties is to allow assignation only in the object initializer or the constructor for your class.

If we consider your Person class, assigning to FirstName or LastName is allowed in the constructor or in the object initializer but you can't assign to FirstName or LastName in other places :

public class Person
{
    public string FirstName { get; init; }
    public string LastName { get; init; }

    // this is a constructor
    public Person()
    {
        this.FirstName = "first name"; // works
        this.LastName = "last name"; // works
    }
}

//this is a random method
public SomeMethod()
{
    Person myPerson = new Person
    {
        FirstName = "first name", // works
        LastName = "last name" // works
    };

    myPerson.FirstName = "change the first name"; // Error (CS8852)
    myPerson.LastName = "change the last name"; // Error (CS8852)
}
nalka
  • 1,894
  • 11
  • 26
  • This isn't a good example though: **if** `Person`'s constructor never assigns to `LastName` and if you do `Person p = new Person() { FirstName = "bob" }` (and _never_ assign to `LastName`) then the program has unwittingly violated the `class Person`'s contract because `LastName` is now `null` despite being typed as `String` (not `String?`), which throws-off C#'s nullable-reference-type checking abilities. – Dai Feb 18 '22 at 06:16
  • @Dai I don't understand why you're saying it's not a good example, "if Person's constructor never assigns to LastName..." Well first of all it's not the case here. Secondly, there's a second condition : having nullable enabled. And if despite all these conditions someone happens to be in the situation you described, CS8616 will let them know there's a problem – nalka Feb 19 '22 at 15:11
  • CS8616 only applies to reference-types - if you're using value-types or have `#nullable` disabled in your `.csproj` then the compiler won't give you any warnings at all. – Dai Feb 19 '22 at 15:13
  • Well that's one more condition (and again it's not met in my example). Indeed, CS8616 won't pop, but you'll get [CS0843](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/cs0843?f1url=%3FappId%3Droslyn%26k%3Dk(CS0843)). If you're disabling nullable well nullable-reference-type checking abilities don't exist so there's no point in having CS8616. – nalka Feb 19 '22 at 15:24
  • CS0843 only applies to `struct` types, not classes. – Dai Feb 19 '22 at 15:25
  • Yeah so you'll have CS0843 with structs and CS8616 otherwise – nalka Feb 19 '22 at 15:29
  • No, that is incorrect: CS0843 is when a `class` has unassigned non-nullable reference-type members, while CS0843 is when a `struct` has unassigned members (regardless of being reference-types or value-types), _not_ when a `class` has unassigned `struct` or other value-type members (that's when you don't get any warnings at all, which is dangerous when the `default(TValue)` a value-type is meaningful, e.g. `enum` values. – Dai Feb 19 '22 at 15:36
2

This also prevents bloated constructors where you have a lot of optional parameters. For example, in the below class:

    public class InitOnlyExample
{
    private readonly string _firstName;
    private readonly string _lastName;
    private readonly string _address;
    private readonly int _zipCode;
    private readonly string _phoneNumber;

    public InitOnlyExample(
        string firstName,
        string lastName,
        string address,
        int zipCode = default,
        string phoneNumber = default
    )
    {
        _firstName = firstName;
        _lastName = lastName;
        _address = address;
        _zipCode = zipCode;
        _phoneNumber = phoneNumber;
    }

    public string FirstName { get { return _firstName; } }

    public string LastName { get { return _lastName; } }

    public string Address { get { return _address; } }

    public int ZipCode { get { return _zipCode; } }

    public string PhoneNumber { get { return _phoneNumber; } }

}
mujtaba
  • 29
  • 2
-2

Unlike set an init-only property can only be set in the constructor or by using a property initializer.