dotnet/csharplang

[Proposal] Required Properties

333fred opened this issue Β· 497 comments

To call it out more explicitly: we know we want to do something here, but we need to find a good syntax. I'm looking forward to seeing suggestions from the community as well :).

My vote is a keyword modifier on set/init. I kind of like required although req and must are ok, too. mustinit seems redundant.

public string Foo { get; required set; }
public string Bar { get; required init; }

mustinit seems redundant.

My idea there was more

public int Prop { get; must init; } 

The full word was for if it's a modifier on the property itself.

Obligatory mention of the verbose syntax:

public not default int ReadonlyIsALongWordButItIsSomewhatClearWhatItMeans { get; init; }

I do not like it, actually, since it tells me something about the property, not that I should initialize it. My vote is with must init.

Here's some more ideas:

public int Prop { get; do init!!; }
public int Prop { get; explicit init; }
public int Prop { get; init!!; }
public int Prop { get; init else throw; }

@333fred

The full word was for if it's a modifier on the property itself.

Got it, so it would be:

public mustinit string Foo { get; init; }

You could argue that the modifier on the property makes it easier to see which properties are required vs. having to potentially scan for the accessors. I still think I'd lean towards required or must on the setter accessor.

What's the reasoning being init props being non-mandatory? I may have missed something, but I imagine that they'll be very rarely used without a req modifier.

Updated the proposal with a new alternative section on "Initialization Debt", which builds on this proposal as a general way of communicating what properties must be initialized in a more flexible fashion.

What's the reasoning being init props being non-mandatory? I may have missed something, but I imagine that they'll be very rarely used without a req modifier.

@Richiban consider the first example:

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

Not everyone has a MiddleName (heck, not everyone even has what Americans would consider a family name either), but I still want Person to be immutable after construction.

Not everyone has a MiddleName (heck, not everyone even has what Americans would consider a family name either), but I still want Person to be immutable after construction.

I get it, but why not simply let them set null explicitly?

var p = new Person 
{
    FirstName = "Alex",
    MiddleName = null,
    LastName = "Fooson"
}

It just seems like a significant weakening of a feature (or, perhaps, a requirement to develop two features) to make the case of optional properties a little easier.


EDIT: Even better: use the concept of property initial values to indicate optionality:

class Person
{
    string FirstName { get; init; }
    string MiddleName { get; init; } = null; // This is optional because it's been given a default value
    string LastName { get; init; }
}

@Richiban

Orthogonal concerns. init accessors state that the property can only be set during initialization, not that they have to be set during initialization. And that doesn't cover set accessors that would be required during initialization but remain mutable after initialization. Splitting them into two concerns covers both bases.

Are required and initializable properties being considered as orthogonal concepts? Or is required an extra that may only be applied to initializable properties?

I'd like to be able to write property declarations that trigger a compilation failure if I fail to initialize the properties in my own constructors:

public class Person
{
    public string KnownAs { get; private required set; }
    public string FamilyName { get; private required set; }
    public string LegalName { get; private required set; }

    public Person(string knownAs, string familyName) 
    {
        KnownAs = knownAs;
        FamilyName = familyName;
        // Compiler error here because LegalName has not been initialized
    }
}

I'd find this useful for expressing intent, especially on classes that I expect will need to be modified in the future as the system develops over time.

For example, if I added

public string Honorific { get; private required set; }

to the above class, having the compiler ensure that I correctly initialized the property in every constructor (including the one in a different file, thanks to a partial class declaration) would be really useful.

@theunrepentantgeek

Interesting scenario. I'm under the impression that this proposal covers the metadata and enforcement of consumers of the class, not of the class itself.

It is an interesting scenario, and one I think could be covered by the initialization debt extension. In that world, those 3 things would be part of the debt, but because they're private to your class you wouldn't be able to publish them as part of your debt contract. They would have to be assigned in the constructor.

I like must since it flows naturally with set and init

required kind of doesnt flow (linguistically) with these two keywords but is useable on property itself not just acessor so privately if we went with keyword on property i would go with required otherwise must

Orthogonal concerns...

Sometimes it's good to pause and think through what someone says before instantly rejecting what they are saying. When we do so, we see that the true orthogonal concerns here are init and set. They do two completely different things and the one solution is not going to fit them both very well.

I suspect I'm not alone in being completely perplexed by the opening statement "The number 1 misconception about init that we've seen is that putting init on a property will solve the issue of properties that are required to be set at object creation". The idea that this is a misconception is worrying. Since it's the no. 1 "misconception" it's clear that I'm not alone in assuming init would be mandatory.

Therefore, let's start with the requirement that it is mandatory. That's what most people will expect it to be and that's what most people will want it to be most of the time.

So how do we address @333fred's conundrum?

class Person
{
    string FirstName { get; init; }
    string MiddleName { get; init; } // many people don't have a first name so it shouldn't be mandatory
    string LastName { get; init; }
}

Well first of all, if it's not mandatory, it has no right being declared as string. It's a string? property. And we can then take the same approach as we do with non-mandatory parameters: provide a default value to make it optional (as @Richiban suggested in his edit above):

class Person
{
    string FirstName { get; init; }
    string? MiddleName { get; init; } = null;
    string LastName { get; init; }
}

Now, MiddleName is an optional initialiser property.

This feature is a nice one and it would be good to add it to set too. This is an orthogonal issues of course; set currently cannot be specified as mandatory after all. But "whilst in the area", let's address that too. And we conveniently already have a keyword that means "mandatory when used in initializers": init

For a mutable Person class where first and last names are mandatory, we then can express it as:

class Person
{
    string FirstName { get; init set; }
    string? MiddleName { get; set; }
    string LastName { get; init set; }
}

Now, init means "must be set at initialisation time". And without set it's read-only after initialisation, which therefore causes no change to what was already proposed for init.

This also then solves @theunrepentantgeek's scenario:

public class Person
{
    public string KnownAs { get; private init set; }
    public string FamilyName { get; private init set; }
    public string LegalName { get; private init set; }

    public Person(string knownAs, string familyName) 
    {
        KnownAs = knownAs;
        FamilyName = familyName;
        // Compiler error here because LegalName has not been initialized
    }
}

private init set is a bit of a mouthful, but I'm not sure how often this pattern would be used. It's likely fine for occasional use. However, if it becomes popular, we'd likely want a neat way of expressing it in a much shorter way. But that definitely is orthogonal...

@DavidArno

So then how do you propose to make a set accessor also mandatory?

init doesn't mean mandatory. It means you can only set during initialization. The requirement that it can only be set during initialization says nothing about whether or not you must set it during initialization. That's where this proposal comes in.

So then how do you propose to make a set accessor also mandatory?

As explained above, by using init set.

init doesn't mean mandatory. It means you can only set during initialization

As clearly stated by the OP, many (likely most) people assume and will continue to assume it does mean mandatory because it makes sense for it to be mandatory for most cases. So I've turned @333fred's proposal on its head with my counter proposal which allows it to be mandatory and meets all the others requirements mentioned in this thread all without adding an extra keyword that has to be used in most scenarios and is only omitted in exceptional cases.

I admit in my first post that I never considered the case of required but mutable properties, but I would still argue for the language design philosophy of:

the shortest code gets you the strictest behaviour

in the name of safety and having a language that will cause developers to fall into the pit of success.

@DavidArno

Nothing about init implies that it's required. You seem to be reading that into the keyword and I doubt that will be the common interpretation. In any case your interpretation of init is not going to happen by C# 9.0 and init isn't likely to be pushed given that records depends on it.

A few points:

  1. What about fields?
  2. Whether a property is required may depend on which constructor is used. For example it's not an uncommon pattern to have a default constructor, in which case all properties are required, ans a constructor which sets everything, in which case none are.
  3. All public fields of non-nullable reference types should be required when you use a default constructor on a struct, as should auto properties. I don't think there's any good solutions to private fields in a struct.

Therefore, let's start with the requirement that it is mandatory. That's what most people will expect it to be and that's what most people will want it to be most of the time.

@DavidArno but that's not what it is. Unfortunately, properties from C# 1.0 were optional by default, and so that's the world we're going to have to live with. In fact, other than conflating init and req, your proposal ends up in a very similar place to the original, except that it introduces massive breaking changes to the .NET ecosystem.

Well first of all, if it's not mandatory, it has no right being declared as string. It's a string? property.

Nullable was not enabled in that code snippet, it cannot be string?. You'll note that my original example did have nullable enabled, and the property was string? in that example. Any proposal we come up with here is going to have to live in this world. Further, being mandatory has nothing to do with the type of the property. It could be perfectly reasonable to default the property to string.Empty for cases without a middle name.

Your proposal paints a nice picture, one I even like. However, it would require changes to large swaths of code, and if we were going to do that I don't think it goes far enough. I'd much rather extend the concept of definite assignment to all properties of an object, and have constructors/factory methods export explicit contracts of "these things must be initialized before this object is considered valid". Then we could have a world like this:

#nullable enable
// Maybe the contract on a default constructor could be implicit, but I'm just putting it on the
// class declaration for example's sake
[RequiresInit(nameof(Person.FirstName), nameof(Person.LastName)]
public class Person
{
    string FirstName { get; init; }
    string? MiddleName { get; init; } = null;
    string LastName { get; set; }
}

public static class PersonFactory
{
    [RequiresInit(nameof(Person.LastName)]
    public static Person MakeMads() => new Person { FirstName = "Mads" };
}

var kristensen = PersonFactory.MakeMads() { LastName = "Kristensen" };
var torgersen = PersonFactory.MakeMads() { LastName = "Torgersen" };
var error = PersonFactory.MakeMads(); // Error: "LastName" is uninitialized"

In this world of mutable last names, no extra keyword would be required here: you didn't initialize the property, so it's part of the "debt" that is exposed from a constructor or factory method. But as much as I like this system, it presumes that properties are required by default, which is unfortunately just not true today, and not likely something that we will be able to change.

the shortest code gets you the strictest behaviour

What we do in the name of backcompat, @Richiban. What we do in the name of backcompat.

What about fields?

The concept of initialization debt could certainly be extended to fields as well.

Whether a property is required may depend on which constructor is used. For example it's not an uncommon pattern to have a default constructor, in which case all properties are required, ans a constructor which sets everything, in which case none are.

That's what the more general initialization debt extension would cover.

All public fields of non-nullable reference types should be required when you use a default constructor on a struct, as should auto properties. I don't think there's any good solutions to private fields in a struct.

Until we get non-defaultable structs I think this is a pretty moot point. It's something we certainly want, but without the feature it doesn't matter.

I still don't understand how init is not required.
Looking at @DavidArno and @Richiban comments and the replies by @333fred made me think about what is being said here.

Please tell me how can something been ONLY setable during initialization, but then not be mandetory. That property is not setable after, so what is the value going to be? Is it default? Is it null?

In this case I agree with what has been set, the property/field needs to be either nullable or need to be preinitialized (not sure if this makes even sense for something that is called init).

Are we really talking about introducing a keyword or attribute just so the developer doesn't have to initialize during initialization?

I rather write

var person = new Person 
{
  Firstname = "John", 
  Middlename = default, 
  Lastname = "Doe" 
}

over introducing a new keyword or Attribute.
And also I like that I am forced to initialize the properties and don't forget something, especially when the class is altered afterwards.

Still don't understand what the state of properties will be if they are not initialized even though they can only be set during initialization πŸ€”

Edit: autocorrection

Still don't understand what the state of properties will be if they are not initialized even though they can only be set during initialization πŸ€”

It's going to be whatever default was provided, or default(T) if none was provided.

Please tell me how can something been ONLY setable during initialization

Don't forget that initialization includes field/property initializers (including property init bodies) and constructors. Lots of places for a default to be set.

Do I understand correctly that C# 9 init for properties should have the same function as readonly for fields? Why have two different keywords for the same purpose?

As for this issue, if both { get; init; } and { get; set; } are valid, I would expect the former one to be required at initialization but impossible to set afterwards (expressed by the lack of set). If I wanted to enforce initialization and allow changing it later, { get; init; set; } would work for me.

Do we expect this to be a commonly used feature? Because an attribute on the property might do the job as well...

@miloush

init is only like readonly in that a consumer can only invoke it during construction (although not necessarily during the constructor), but it doesn't prevent the underlying field from being mutated later.

init as it is expected to ship in C# 9.0 doesn't require initialization, hence this proposal which seeks to add the ability to require initialization to init properties, set properties and fields.

MgSam commented

Why not just use the ! operator? This seems highly analogous to null-checking of constructor parameters.

class Foo
{
    public int Bar! {get; init;}
}

@MgSam

This seems highly analogous to null-checking of constructor parameters.

I don't get the analogy. !! won't enforce you call a particular constructor, and a required property doesn't care if you assign it to null, only that you assign it.

MgSam commented

@HaloFour They are both indicators of requiredness. Though admittedly slightly different forms of requiredness (null check vs required assignment). I think it would be much better than adding yet another keyword for what on the surface is an extremely similar concept.

! has already been overloaded several times. => also means similar but different things depending on where it is used.

@MgSam

I personally don't see the similarity. I think required assignment and required non-nullability are quite orthogonal. IMO it'd be very strange that a property adorned with !! would be happy with a null assignment. I doubt it makes it any less complicated to implement by reusing a character sequence used for another feature.

I tend to agree with @RUSshy here that there is no motivation or problem discussed that this proposal is trying to solve.

The best I can do from the Motivation section is that people misinterpret the upcoming init to do something else, and because that something else cannot be currently done in the language, rather than "sorry, that's not what you think it is" we need to be able to say "sorry, what you think it is can be done this way".

First, that suggests that the init as proposed is not very intuitive to people, and second that there is no reasoning behind why what people think it is needs to be done.

To rephrase this, the proposal says "let's have a way that allows properties to be required to be set during construction", the question then is "why we need such properties", what current or future problems "required properties" solve?

It looks a lot like what the nullable is currently doing with fields and constructors. The only argument against is that "nullable has to be on" and we cannot introduce such requirement into existing code. But the proposal is suggesting new language keyword, so in any situation that you can use the proposed keyword you can also turn on nullable.

Additionally, it goes, how does it work with structs for which default is not a valid option? You declare the backing field as nullable. You can still have the property return the non-nullable equivalent.

Finally, why is this for properties only? If there is a demand for required properties, then I don't see why there wouldn't be a demand for required fields; or even methods that are required to be called during initialization. Any choice of keyword should make sense in case its usage is extended to other member types. As a consequence, whatever mechanism is chosen, it should be associated with the setter only, not the whole property.

As for the proposed set of keywords, must works in a lot of the contexts set/init/get/add/remove and because the accessors are all verbs (and normal methods tend to be verbal too), it is clear that you must set the property or must init it. It is not great with fields. It also does not indicate when you must do whatever you must do, in which respect mustinit is better, but obviously it is a little suboptimal with mustinit init. I don't like explicit due to its current interface connotations, so it looks like required is favorite of mine too. Generally past participles might work the best here, e.g. initialized - which has the precedence of compiler complaining something is not initialized before use. I prefer required to req, this is C# not assembly.

@miloush

There have been many requests and proposals to force object initializer syntax for construction, either via constructors or through other means. This is a requirement that comes more to focus with nominal records, and the "misconception" that init already does this already highlights that there is enough demand for a feature of this nature.

As it is with init, this proposal is orthogonal to NRTs. A required property or field dictates that the member must be assigned, not what the member must be set to. You can compose the three features completely separately from one another:

public string? Foo { get; set; } // mutable after construction, nullable, optional during construction
public string Bar { get; set; } // mutable after construction, non-nullable, optional during construction
public string? Baz { get; init } // mutable only during construction, nullable, optional during construction
public required string? Fizz { get; set; } // mutable after construction, nullable, required during construction
public required string Buzz { get; init; } // mutable only during construction, nullable, optional during construction

This is a requirement that comes more to focus with nominal records

Right, and for me that is the main weakness of the proposal. It only makes sense in a nominal record because nominal records effectively replace constructors with init as an initialization strategy. Positional records, classes and structs have constructors, so if you want something set, you make it a parameter.

So I guess the question is this: where would required properties be used outside of nominal records?

Edit: maybe it would make sense to have all record properties marked with data be required? And if you want an opt-out, use the longer form with init? This kills the thing as a general mechanism for requiring arbitrary stuff to be done at initialization time, but my opinion is that's fine given constructors exist.

@HaloFour - that's a very good summary of the options. However, I can't see how this one works?

public string Bar { get; set; } // mutable after construction, non-nullable, optional during construction

If it is optional during construction, what value does it have it not set during construction, because default(string) is null, and Bar is explicitly non-nullable. Or does this create a new NRT warning when the object is created that value has been left in an "invalid" state?

Or does this create a new NRT warning when the object is created that value has been left in an "invalid" state?

Yes, that's the idea.

there is no motivation or problem discussed that this proposal is trying to solve.

You clearly have missed the discussion in #2328.

Why not just use the ! operator? This seems highly analogous to null-checking of constructor parameters.

Actually we want to be able to configure run-time null checks with object initializers too, right? πŸ˜€ Hand-written run-time null checks will otherwise be tedious and dis-connected from a property's declaration. So perhaps best to not preclude a nice future solution like (for example)

public string Prop1 { get; req set; } // Prop1 required in initializer
public string Prop2 { get; req! set; } // Prop2 required in initializer and run-time null check performed

I still don't get the insistence to use object initializers as the default way object creation in C#. We all know that, object initializer was introduced as just a syntax sugar. It did not have checks for safe object creation. Now when the nullability has been introduced to the language, the hole that object initializers cannot safely initialize non-nullable properties- is exposed. Two major language versions- C# 8 and 9 are being shipped with this hole. They also did not play nice with immutable properties, which is being solved with new language features. And while promoting the use of initializers, instead of redesigning it to play the role of constructor (which would technically be a breaking change but real code breaks would be within expectation with nullability checking enabled), new keywords and syntaxes are being proposed. This very much looks like bloat to me. Kotlin just uses constructors, with named parameter when preferred, without requiring all these bells and whistles and ensures null-safety. Their data class definitions and other class definitions are short, safe and sweet.

My poorly worded issue #2558 can be viewed for some previous discussion.

I still don't get the insistence to use object initializers as the default way object creation in C#

I don't think anyone is insisting this is the default way to create objects. Simply that it should be treated as a first-class scenario that should work well with other lang features.

@CyrusNajmabadi Why not redesign it to play constructor-like role then? By which I mean, if nullability check is enabled, in any object initializer call-site, compiler will ensure non-nullable properties are initialized to non-null values. If nullability check in not enabled, nothing changes syntax-wise, but internally it is not just the syntax sugar it used to be.

@gulshan

Constructors are positional and inherently brittle to changes. Adding new optional members would require the addition of an overload, and you'd have to be careful to keep the new members at the end of the list otherwise you'll break all consumers. Constructors for data types of more than 5-6 properties are simply cumbersome, even if you treat it as a quasi-initializer syntax.

@HaloFour Like I've said, object initializers should have been redesigned to ensure safe object construction.

@gulshan That's one of the options in the proposal, if I am reading what you are thinking of correctly, and it has been discussed above:

We could also go the route of attempting to retconn nullable and flip the default for properties when nullable is enabled. If a default value is provided for a property, instead of warning at the object definition site, we'd warn or error at the object creation site. We'd have a few issues to work through to make this work, though:

  • Are you required to provide a default for struct types? If not, how do you specific that a struct property must be initialized?
  • Is this going to produce a warning or an error? What will it do in non-nullable enabled code?
  • Is this expressive enough? Namely, does it communicate the intent of the developer well enough that we feel confident in reading the code later?
  • How breaking of a change is this? Doing this would already have been a pretty big semantic break from pre-C# 8 code, and it's an even bigger break now that nullable has been out for a while.

@SingleAccretion I think the focus should be on object-initializers being repurposed to play the role of constructors. Then both immutability and non-nullability checks within object initializers will be performed by the compiler similarly they are done within a constructor. Modifiers like init and req should not be required.

@gulshan

I think the focus should be on object-initializers being repurposed to play the role of constructors.

That is what these proposals are focusing on. This is an attempt to get the behavior of constructors with the flexibility of initializers.

Then both immutability and non-nullability checks within object initializers will be performed by the compiler similarly they are done within a constructor. Modifiers like init and req should not be required.

I don't understand how that would be possible. Object initializers already mean something and that can't change. Without init there is no such thing as a property that can only be set during initialization. Without req (or similar) there's no way to enforce that the property is required to be set during initialization.

@CyrusNajmabadi Why not redesign it to play constructor-like role then? By which I mean, if nullability check is enabled, in any object initializer call-site, compiler will ensure non-nullable properties are initialized to non-null values.

This is the space we are looking at.

@SingleAccretion I think the focus should be on object-initializers being repurposed to play the role of constructors. Then both immutability and non-nullability checks within object initializers will be performed by the compiler

This is the space we are looking at.

Whatever the syntax for required initialization ends up being (I don't care if it's the default for the bare init keyword or something more verbose like req init), I think it's a mistake to ship records in C# 9 before this feature lands, and to have records not use it by default for auto-properties. There's a reason that this β€œmisconception” that record property initialization is mandatory is so widespread: mandatory initialization for properties is exactly what people are expecting and want from record types coming from F#, Rust, and more. The whole benefit of records in C# in the first place is not having to write a bunch of boilerplate keywords to get reasonable defaults for a record-like type, compared to fighting against the defaults when using the class keyword instead. If those defaults don't go all the way and still require additional modifiers in order to get what people expect out of a record type, then in my opinion records will be a relatively lackluster feature.

Also, consider that if creating records where properties must be initialized is more verbose, then users who are already using NRT might just not bother, since it will warn them anyway upon object construction. Therefore, I worry that unless this mandatory initialization is the default for records, then this proposal will compete with NRTs.

@Serentty That's why I'll be using positional records when C#9 is released.

Why can't it be just string FirstName { get; req; } or string FirstName { get; required; } without init? What's the benefit of using two keywords instead of one?

Because there are four scenarios that the LDM want to support:

  • read/only properties that may be set in an initialization block - init
  • read/only properties that must be set in an initialization block - required init
  • read/write properties that may be set in an initialization block - set
  • read/write properties that must be set in an initialization block - required set

Then why can't it be like this?

  • read/only properties that may be set in an initialization block - init
  • read/only properties that must be set in an initialization block - required
  • read/write properties that may be set in an initialization block - set
  • read/write properties that must be set in an initialization block - required set

Because adding that init there feels really redundant.

I like @wanton7's proposal. While it may be a bit asymmetrical, it helps avoid modifier stew for a common use case, and a use case that I think should be encouraged.

I think the symmetry is better. A bit verbose, yes, but it makes the distinction clearer.

The issue I see with it is that the safer option being the more verbose option is usually trouble. Think of how many const modifiers could be used in C++ but aren't because it's more verbose. The clarity of the name is something that can be resolved easily with a quick glance at the documentation, but its verbosity is something that subtly impacts the way that people write code in the long run.

Whatever option is selected, I hope deserializers can use it. If I define a DTO that looks something like

#nullable enable
public record Schema
{
    //NB: I have deliberately omitted all modifiers
    public string Name;
    public List<Table> Tables;
}

I expect the following:

  1. When I consume a Schema, I can rely on Name being not null and Tables being not null
  2. When I create an instance of Schema by hand, I can rely on the compiler warning me if I don't initialize either property
  3. When I invoke a deserializer from JSON or DbDataReader or whatever, it can validate that all properties are initialized on the instance of Schema before returning it to me, either by trapping an exception or reading the metadata.

I just want to note this feature will be super useful even when using constructors because tooling will be able to auto-generate/auto-update constructors based on required properties/fields. Which will save a lot of time. And of course bugs will be reduced because complier errors will result if one forgets to add a required property to a constructor which is in use somewhere in the code.

IMHO the problem up till now is programmers have no way to express the intent of "initialisation required" at the field/property level. In Swift, a programmer simply doesn't specify an explicit default to express this*. But that doesn't work in C# due to automatic default values. We work around this by using constructor arguments as a required property list (which requires maintenance) or via various attributes or by using validators following initialisation etc. All of which have issues and complicate code.

I think adding at field/property-level a consistent way to express "required" will be a massive step forward for C# and make tight and robust code a lot more simple to create. I'm really looking forward to it. πŸ˜€ It's the number one C# feature I'm waiting for.

* NB optionals (nullables) in Swift work differently and do have an automatic default and hence there is similar ambiguity for these about whether the programmer β€œrequires initialisation”

alrz commented

I find myself wanting this today. Even though I'd prefer "Retconn nullable and property defaults" option, here's my vote:

public string FirstName { get; } = init;
public string LastName { get; } = init;

@alrz I also prefer the retconning, to the point where I would be happier if nominal records were cut from 9.0 and introduced in 10.0 with required properties by default.

alrz commented

Yup. Any time you want "required properties" usually you want all of them to be required and perhaps some to be optional. It would add much boilerplate code to specify the opposite. This is now more evident with NRT when you can actually annotate such properties with a ?. That should be enough hint for the compiler to infer the intent.

The good news is that it seems that the syntax for automatic record properties (the data keyword) has been postponed to C# 10, so there's a chance that it will use required initialization.

@alrz Do you mean this would be used for optional init properties?

public string FirstName { get; } = init;
public string LastName { get; } = init;

Because it doesn't allow you to specify default values. But one currently in preview does

public string FirstName { get; init; } = "John";
public string LastName { get; init; } = "Doe";

But did you mean that for required? If you did I think it would made code harder to read

public string FirstName { get; init; }

for optional and

public string FirstName { get; } = init

for required

alrz commented

@wanton7 Good point. Though I believe any syntax here is redundant. the type being nullable is enough and it's already there. We should just warn on object construction (new) rather than inside the type.

Since that ship has sailed I think the only workable approach is to do this only for data properties as @Serentty said.

To risk an oversimplification, we are looking at "init" as meaning "can be initialized" and "req" as meaning "must be initialized". The idea of initialization debt is appealing and it suggests to me that initialization must occur, obviously, before use i.e. before 'get' is invoked.

I know that the proposal involves initializing at specific places/times related to construction, rather than at any point prior to use of 'get'. However my main goal in using non-nullables is to prevent nulls from being propagated throughout my code, and for that purpose it is perfectly acceptable to leave properties uninitialized until a time prior to use. A perfect example of this is when an object is going to be initialized (and then validated) through a UI interaction. From a domain modeling point of view all properties may be required, but in terms of the process of obtaining those required values, the properties will not be initialized at construction time or through an initializer expression but later on as part of user interaction. It would not be satisfactory to mark all such properties as nullable in the model because that does not match the domain meaning of the object, nor does it allow us to take advantage of non-nullability features in the language.

Consequently one part of my suggestion is to relax the required initialization points so that initialization can occur at any time prior to 'get' being called. This will enable the scenario I describe and is consistent with the goal of not allowing nulls to propagate throughout a body of source code.

The other part of my suggestion here is to fill in a logical gap that arises in this approach. In addition to "can be initialized" and "must be initialized", we need to be able to express "is initialized" or "has been initialized". Let's look at a simple example of a non-nullable uninitialized property supported by a required nullable backing field:

public Wheel FrontLeft { 
      get => this._frontLeft ?? throw new UninitializedPropertyException();
      set => this._frontLeft = value;
} 
private Wheel? _frontLeft;

Allowing the delayed initialization that I am suggesting for 'req', the auto-implemented version of this would be

public Wheel FrontLeft { get; req init; } 

Now, the piece that is missing in the context of delayed initialization is being able to test for whether the property is initialized or not. As it is written above, it is hard to use the property without risking an exception. The exception makes sense from a domain model point of view because the property is intended to be required. However when using the object prior to completion of the delayed initialization, the exception presents a danger. We need to be able to test at run time whether or not initialization has occurred. I propose recycling the "is" keyword for this:

public Wheel FrontLeft { 
      get => this._frontLeft ?? throw new UninitializedPropertyException();
      set => this._frontLeft = value;
      is => this._frontLeft != null; 
} 
private Wheel? _frontLeft;

The auto-implemented version of this would be:

public Wheel FrontLeft { get; req init; is; }

The addition of 'is' in the nullable context would enable the compiler to adapt nullability checking so that a test such as "this.FrontLeft != null" would not result in assuming that FrontLeft is nullable. On the contrary, this.FrontLeft would be considered a non-nullable except for the specific case of being compared to null (although, some thought should be given to use of such a property in 'is' pattern matching). When nullability checking is disabled, 'is' would be syntactically disallowed, much as "?" on a reference type is not accepted syntax when nullability checking is disabled. We could also extend the use of 'is' to value type properties, where it would imply that the backing store is a nullable value type (this increases expressibility over the nullable-oblivious language, but not in a breaking way).

An example benefit of being able to use 'is' explicitly in the non-auto-implemented case is it enables us to work more easily with the dependency property system. Instead of being backed by a private field as shown above, the property may be backed by a dependency property, which obviously is a different implementation of a nullable backing store.

In this way we can achieve multiple goals: domain modeling of non-nullable properties; good interaction with the UI system; allowing delayed initialization; and avoiding boilerplate for required constructors.

Thanks @sjb-sjb for this interesting proposal.

Consequently one part of my suggestion is to relax the required initialization points so that initialization can occur at any time prior to 'get' being called.

The downside of allowing initialisation after the object initialiser is that we can no longer take advantage of compile-time checking and warnings/errors to ensure our object initialisers include all required properties. These compile-time checks were to my mind one of the biggest benefits of this proposal and would allow e.g. auto-population of object initialisers with all required properties via IDE tooling. This would greatly simplify initialisation and use of third-party classes etc. Pushing checks for required initialisations into run-time (or even compile-time) errors at point of access would be much less helpful IMHO.

In your proposal there is also the problem of init; and req init; now having different meanings for the word "init".

As an alternative idea, why not use req set; for your use-case of initialisation required but might happen after the object initialiser but before use (and enforced by compile-time or run-time checks)? This would keep the meaning of "init" consistent (i.e. property can be set during constructor or object initialiser) and would also allow the object initialiser compile-time checks described above.

I also think if we have is it should be valid for all types (not just nullables) as req ("required") implies the default value is meaningless so having a check for "do we have a meaningful value?" (i.e. "has this been initialised?") is relevant to any type. (Note also the user-initialised value might be the same as the automatic default value (e.g. user initialises integer to 0) so a comparison check doesn't work either in your case without special adjustment or in general.)

Hi @markm77 thanks for the comments.

I don't think init; and req; init; would involve different meanings for 'init'. It would always mean "can be set during construction/initialization" as compared to 'set;' meaning "can be set at any time".

I agree it would be useful to distinguish at-creation initialization tests from subsequent run-time initialization tests. However I am not as sure whether allowing req; with set; would make sense. I believe that "set" is something that could occur repeatedly and it generally allows you to set the value back to null (when #nullable disable), which is not the intention here. Arguing on the other side, when #nullable enable one could say that the non-nullable property can't really be set back to null anyway ... on the third hand I wonder if that might lead you down the path to different behaviors under different settings of #nullable.

Also would there not be some redundancy inasmuch as specifying "is;" already could be used to infer that "get" should throw an exception if the backing store is null?

Maybe one could define 'is;' so that: (a) the compiler adds code to the 'get' accessor to throw an exception at run-time if it returns null and (b) if the property is auto-implemented then the backing store is nullable (c) comparisons to null do not result in the inference that the property is nullable. Then we could reserve 'req' and 'init' for the at-construction meanings in the original proposal.

If we did that then I believe 'is' and 'req'/'init' would be orthogonal to each other. If you specified both 'is' and 'req' then outside of the initialization/construction code the complier could optimize away the run-time test in 'get' because the 'req' will have already forced assignment during initialization/construction. Technically 'is' would still have meaning when combined with 'req' since during construction the property may or may not have been assigned.

don't think init; and req; init; would involve different meanings for 'init'. It would always mean "can be set during construction/initialization" as compared to 'set;' meaning "can be set at any time".

Okay, so you want to extend both init; and req init; to include initialisation outside the object initialiser. I understand although feel this undermines initialisers and the convenience of compile-time checks at point of object creation.

The suggestion of req set; was made because I understand you do actually want to perform initialisation with the "setter" rather than in the "initialiser". But I agree this means the property is not read-only.

Do you really have a hard requirement not to use constructors or object initialisers but want to only allow use of the "setter" once/prior to first access? This seems to trample over the existing concepts in C# (constructors, object initialisers) to create a third kind of read-only property. A high degree of complexity and confusion are likely outcomes IMHO....

@markm77, in my last post I am not proposing to extend req or init at all, but to add a separate β€˜is’.

I gave what I think is a pretty good use case in my earlier post, relating to initializing values from the UI. This is in fact a live situation as I am actually doing this and encountering this problem. The compile-time features of req and init really do not address this situation. Of course glad to hear any constructive suggestion people may have addressing that use case.

I believe it is reasonable to have both run time and compile time features supporting uninitialized non-nullables.

Thanks @sjb-sjb , probably best for me to stop responding to free up this thread. My main discussion point with you was about allowing initialisation after the object initialiser but seems you are moving away from that.

@sjb-sjb

I don't think is is required for that. Whether or not the backing field is null (or any other value) doesn't tell you whether or not it was initialized. Setting the property to null would be perfectly legal and would satisfy required properties.

I think what you're looking to achieve, namely throwing at runtime if a specific field has not been set to a non-null value, could be easily accomplished through #140 :

public string Name { get => field ?? throw; set; } 

@HaloFour, the backing field being null does tell me whether or not the property was initialized, for a property having a non-nullable type which is the use case here.

I agree #140 could be part of the solution but it does not address the need to be able to test whether or not the property has been initialized. That is the purpose of β€˜is’.

@markm77 I am not moving away from the idea of post-constructor initialization at all, that is still the main point of my suggestion. Well it may be a little confusing to use the word β€œinitialization” here since that word usually refers just to a few specific times (the page on init lists them) while I am using the word on a more general sense.

@sjb-sjb

the backing field being null does tell me whether or not the property was initialized, for a property having a non-nullable type which is the use case here.

Required properties doesn't care about the value, it only cares that the property setter was called during initialization. There is no runtime component and it's not intended to be a method to enforce NRTs, beyond stating that the property is required and warning if the consumer sets it to null.

If you're looking for a proposal that can throw at runtime on a property getter if a certain condition is met it sounds like you should open a separate discussion.

the backing field being null does tell me whether or not the property was initialized, for a property having a non-nullable type which is the use case here.

If this functionality was implemented, it would apply to all properties, including nullable ones. Meaning that setting null during initialisation is still a valid choice. To achieve your example of

public Wheel FrontLeft { get; req init; is; }

I think it would need to lower to:

private Wheel? _frontLeft;
private bool _frontLeft_init = false;
public Wheel? FrontLeft { 
      get => this._frontLeft_init ? this._frontLeft : throw new UninitializedPropertyException();
      set => { this._frontLeft = value; this._frontLeft_init = true; }
      is => this._frontLeft_init 
} 

@spydacarnage that’s one way to look at it. But if β€˜is’ is supposed to tell us whether or not the property has been initialized then it should be true when the property has a valid value. For a nullable, all values are valid. So rather than have another field to indicate whether or not a nullable has been initialized, I think we could just have: is => true for nullable types. The question mark for me is on value types: whether including’is’ should imply that the backing field is nullable. I tend to think it should.

I agree this could go in a different thread, either a new one or possibly #140.

I have opened #3963 for this.

@333fred , what's the interaction of this proposal with data ?
I'm super excited by this proposal happening in some form, BTW, so thanks a lot for championing it.
I like the 'initialization debt' concepts too.
And having some sort of a factory pattern support would be very nice. Everyone uses different naming for them, and it would be sure nice to have some way to indicate them in the language (and enable how it would play into the other parts of this proposal)

I think the 'retcon' concept is an interesting idea, even though at first it seems like a huge break.
I'm guessing that most people who are excited/have implemented NRT would wish that the behavior that's coming out with this proposal was the way it had come from at the start. And are the type of people who'd be OK with refactoring around the break, and pretending that the previous behavior never existed - in exchange for clean behavior going forward.
One downside I can think of is that a person would have to be aware of the nullable setting of a codebase if they're just casually reading it in GitHub etc, in order to understand the behavior of the same lines of code. It kind of introduces C#10 version A and B...
It's still worth seeing what people think of the idea, I'd say. Cheers

what's the interaction of this proposal with data ?

They're separate features. If we even have data, it may imply this, or it may have nothing to do with this.

Thanks Fred.
If anyone has links to the data related proposals/discussions, I'd appreciate that. I'm having a hard time finding them now.

So I understand that the init means that you can only set a property during initialization, and that is does not mean that a property is required to be initialized.

But with nullable being enabled, the current warning is literally: "Non-nullable property [..] must contain a non-null value when exiting constructor".

It seems very natural to make the very combination of a non-nullable property together with init to mean "must be initialized during construction". It feels more logical than to add yet another keyword in front of your property.

The previously mentioned problem is that if nullability is not enabled in your project, you cannot enforce the requirement.
That is true, but currently, nullability enabled with an init on a non-nullable property, always gives a warning which make it harder to use.

@mharthoorn you're basically advocating for the alternative labeled "Retconn nullable and property defaults" in the original post, right?

As this is basically a hint for the compiler to move the warning from the property to the contructor I would strongly vote for an attribute on the property. Either [Required], [NotNull] or something like [MustInit].

But as I understand correctly this is only an issue for nullable types so maybe the [NotNull] attribute would fit best. The compiler can then switch to a different warning or error when it finds that attribute.

@ericsampson, I think i am advocating for the "initialization debt" paragraph. But I'll try to be more explicit. The core is that it's not a matter of the class definition to have warnings in them, since initialization can happen else where. The warnings should happen at actual construction / initialization. It would help the adoption of the nullability feature a lot, because it gets rid of a lot of unnessessary warnings.

Current behaviour:

#nullable enable
class Person
{
    string FirstName { get; init; }  // warning: non-nullable property must contain a non-null value
    string? MiddleName { get; init; }
    string LastName { get; init; }   // warning: non-nullable property must contain a non-null value
    string City { get; init; }  = "Roslyn Washington" ;
}

var p = new Person { FirstName = "Mads" } 
// No warnings.

But:

  • FirstName: Should have no warning, because it's initialized.
  • MiddleName: should have no warning since it can remain null.
  • LastName, should have a warning because it is not initialized.
  • City: Should not have a warning, since it has a default value.

Proposed behaviour:

#nullable enable
class Person
{
    string FirstName { get; init; }  
    string? MiddleName { get; init; }
    string LastName { get; init; } 
    string City { get; init; }  = "Roslyn Washington" ;
}

var p = new Person { FirstName = "Mads" } 
// Warning: LastName is a required property and must be initialized during construction.

With a constructor:

If the class did have a constructor, It becomes a little bit less obvious, if it leaves properties empty.

#nullable enable
class Person
{
    string FirstName { get; init; }  
    string? MiddleName { get; init; }
    string LastName { get; init; }  
    string City { get; init; } 

    public Person(string city)
    {
        this.City = city;
    }
}

var p = new Person("Roslyn WA") { FirstName = "Mads" } 
// Warning: LastName is a required property and must be initialized during construction

With nullable disabled,

it's a different story, but there would be a lot less need for disable nullability if we dealt with it this way.

Why not extend it to more general scenario which can be applied to both properties and parameters, with more flexible requirement customization.

requires {
    expr; expr; expr; ......
}
void Foo(int v) requires {
    v > 3 && v < 10;
    v % 2 == 0;
} {
    Console.WriteLine(v);
}

class C
{
    private object v;
    public object Value 
    {
        get => v;
        init requires {
            must;
            value is int x && x is > 5 or < 1 || value is string;
            value is not null;
        } {
            v = value;
        };
    }
}

C x; // error: must init Value
C y = new C {
    Value = 2
}; // error: "value is int x && x is > 5 or < 1 || value is string" cannot be fulfilled
C z = new C { Value = "test" }; // ok
class Person(
    string FirstName, // public, required, get, init
    string LastName // public, required, get, init
) {
    public string? MiddleName { get; init; } // optional
}

Person p = new("John", "Doe") {
    MiddleName = "C#"
};

Required properties in this syntax can only be public, required, and init only. Any other cases should be implemented using standard constructor.

Looks like that properties of Positional Record should be required by default. See: [Proposal]: Allow skipping constructor of Positional Records #4178

We should get rid of following compile error:

public sealed record SequenceStats(double Average, double Median, double StandardDeviation, double Minimum, double Maximum);

var scores = new [] { 1, 2, 3 };

var static = new SequenceStats // CS7036: There is no argument given that corresponds to the required formal parameter 'Average' of 'SequenceStats.SequenceStats(double, double, double, double, double)
{
    Average = scores.Average(), 
    Median = scores.Median(),
    StandardDeviation = scores.StandardDeviation(),
    Minimum = scores.Min(),
    Maximum = scores.Max(),
};
jods4 commented

This thread seems to focus a lot on the syntax to mark properties required.

Personally, I don't have much use of the "required" concept but I'd love if the "uninitialized nullable ref" problem was handled better. The two are related but may weigh pros and cons differently.

Because I care most about nullable initialisation, I am definitely in favor of the implicit (no syntax/annotation) approach.

I have tons of properties that look like that

string Name { get; set; } = null!;

I'll be honest, I don't think looking like either of those is a big improvement:

[MustInit]
string Name { get; set; }
string LastName { get; req set; }

What is a huge improvement IMHO is moving the error from ctor to construction site.
If I have

#nullable enable
string Name { get; set; }

Then obviously I don't intend Name to ever be null. So if the ctor doesn't set this property, the caller should do it in an initializer. If it doesn't it's a violation of the type system, so it's normal to report it.
Whether the fix should be adding an initializer, modifying the ctor, or making the prop nullable string? can't be guessed by the compiler.

Let's look through this lens at some pros/cons from above:

Should it be a warning or an error? Today C# has decided to use nullable warnings (you can always "treat warning as errors"). I'm talking about moving where CS8618 is reported, so it would naturally stay a warning.

Is this a breaking change? In some cases, I'm afraid.
Generally it won't if it only applies to nullable-enabled code. Because today any property that is not initialized after construction is already reporting CS8618, after this change there would be strictly less warnings, not more.
I see two big exceptions, though.

  1. New warnings w/ library types. A library might have disabled CS8618 when it's compiled. With this change, the warning has moved from library compilation to end-user compilation. It's probably good because legit cases for this aren't newing the class but reflection, serialization, etc.
    It's still a break, though. It's mitigated by the fact that it's only a warning and could be turned off, but I don't have better ideas.

  2. What about structs? If a property is of non-nullable type and ends up as null after construction, it should be a warning. No reason to behave differently than classes. Unfortunately this is breaking because CS8618 isn't reported for structs today. I think it's for the better to warn here; although this could be mitigated by using a new CSxxxx warning for structs and making it opt-in.

What about factories and other partial initialisation situation? Tricky. It would be awesome to track these correctly and that's basically the "initialisation debt" idea above. If awesomeness can't be achieved, moving the ctor warnings into factories would already solve lots of other real-world use cases and be a big improvement.

In conclusion: what has a huge value for me is proper null handling, I don't care as much about the more general concept of "required initialisation".
Nullable dataflow analysis is a general concept that should "just work" without annotations, so req doesn't feel right.
To me CS8618 was an error. It leads to a lot of boilerplate "fake" initialisations, that compromise correctness. Not only does string name = null! feel bad, there will be no warning if I new Person() without an initialiser and then proceed to do person.Name.Length.
From this point of view, fixing it implicitly is a no-brainer.

We discussed this LDM on Monday (December 7th 2020), with a new revision of the proposal: #4209.

@jods4

What is a huge improvement IMHO is moving the error from ctor to construction site.

That's what #2109 was originally about, which was merged into this. So, presumably, you'll get exactly that once this issue is closed.

What's the reasoning being init props being non-mandatory? I may have missed something, but I imagine that they'll be very rarely used without a req modifier.

One such case could be a report where some of the data is not present, depending on the year. You still want immutability for the report, but not every year you would have, for instance, M&A costs.

Input from a line-of-business application developer:-

A beautiful thing that I used to enjoy in F# was introducing a new field into one of my record types... and have all the construction sites break on re-compilation.

I have missed this feature so much in C#, but I'm too lazy to make all my classes have ctors with all the necessary args to initialize all the (read-only) properties.

Today I got to grips with C# 9 records for the first time, and looked forward to that sweet "must init" goodness.... to little avail!! Unless I've read it wrong, the only way to get such a thing in C# records is to only use positional records / primary constructors, and never introduce a standalone property definition.

The problems with positional records / primary constructors are:-

  • hard to put XML comments / documentation on the params / auto-properties (although I understand this will be sorted out soon)
  • with two or three or four params, the primary constructor route is ok, but if there are significantly more, it starts to look messy and for readability it's nicer to have all the properties (and their XML comments) listed out in the body of the record (or class)
  • harder to change existing classes to records if you have to delete all the properties and re-write them into the primary constructor
  • hard to add additional attributes to the auto-properties, unless you end up duplicating them in the record/class body.

The simple addition of:-

public object Prop { get; req init; }

would seem to give us the best of all worlds. Add req to make it required; leave out req if it's not required... an improvement on F# even.

The simple addition of public object Prop { get; req init; }
would seem to give us the best of all worlds. Add req to make it required; leave out req if it's not required...

I also like the simplicity of this idea, it's how I thought things were going to go. Now I'm not sure it is?

EnCey commented

Just wanted to chime in and add my vote for such a feature.
Personally I like must set or must init because of how natural it reads, I think it's even somewhat self-explanatory, and because "must" is more concise than "required".

I would not be opposed to a keyword for the entire property, though.
I'm not sure if this analogy has been made yet, but in terms of how it fits into the language and how difficult it is to grasp for new developers, I believe it makes sense to compare this feature to abstract:

  • if I make my property abstract, I must declare / override it in my derived classes
  • if I make my property required, I must assign it a value when I create an instance of my class
  • in either case, the compiler will give me a friendly hint if I've failed to comply with these demands

While technically "required" will only affect the set portion of a property, I believe that conceptually you would probably view the entire property as "required".
As for readability, a keyword "in front" of the property makes it (arguably) simpler to quickly scan a class's properties, especially if we have longer property types and names like IReadOnlyDictionary<string, SomeOtherVerboseType> MyVerboseProperty {}

public required string Firstname {get; set;}
public abstract string Lastname {get; set;}

As a final point, I must note that "abstract" and "required" both are 8 characters long, which means they would nicely align, which I can't help but find appealing.
I believe it's also an argument that "required" wouldn't be too verbose seeing as I've never heard anyone complaining about "abstract" being verbose. Furthermore, auto completion features of IDEs take care of typing anyway, and "required" doesn't overlap with any other keywords, hence it would work well there too.

Excuse my ignorance but why not set the required property through constructor parameters? If you make the default constructot private and create a constructor that takes the parameters to set the required properties, doesnt that already solve the problem?

In my opinion object initializer is more elegant and error proof than ctor or default ctor with init props. Imagine you have a ctor with 10 string props, a programmer has to pay extra attention about what he passes to ctor arguments. He can of course use named arguments, but he is not forced to. required init forces programmer to pay extra attention when assigning values, and provides a language solution for init/set props misuse during object creation (not initializing of what is required). I think programmers will more likely use { get; required init; } than { get; } with ctor.

init tried to solved a problem of writing constructors for props that shouldn't change after object creation (i.e. { get; }), but didn't provide a feature that ctor always had, i.e, constraint that you have to set a value for all arguments without default values that are defined in ctor.

However I don't know if required init; would allow for changing prop from the inside of the object e.g. { get; private set; required init; } because { get; private set; init; } is also not possible (this is a bit disappointing, but { get; private set; init; } can be achieved with a backing field).

Summarizing, 1) it's much faster to write required or other keyword than complex ctor; 2) this solves the biggest problem with init props - trusting programmer that the prop will be initialized; 3) removes pointless default! \ null! assignments; 4) adds language level feature to ensure init prop was initialized; 5) for me current init implementation without required is good only for DTOs and other externally initialized objects (when you don't create object by yourself, i.e. by using new).

I've removed the initial specification from this proposal, as it is now in PR here: #4493. When merged, I'll update the link to point to the merged specification itself.

I just added a new parameter to a base class's constructor.
Then I had to refactor 50 derived classes to pass in that parameter.
Man, I wish I had required properties and a DI framework that would inject using them instead of constructors.

I guess I'll throw in my two cents. I am going to cover 3 areas that I think this proposal should deal with. I hope this helps someone out there.

The warning location when nullable is enabled

The issue I have with init by itself is that when nullable is enabled, the compiler will give a warning to types who have not been initialized in the constructor. The warning is: "Non-nullable property 'ActionName' must contain a non-null value when exiting constructor"
image

This is technically correct behavior, but the location of the warning feels inconvenient. The warning should show up on object initializers. Note: this specifically only applies when nullable is enabled.

image

Using required with init properties

Summary: Properties are required to be set during object initialization and cannot be mutated afterwords.

The following should throw a compiler error because the "required" properties ActionId and ActionName are not specified during object initialization. The important bit in this example is that it forces a programmer to initialize the required properties which are normally set to default(T). Note: It should also require value types to be initialized.

public record Toast
{
    public required int ActionId { get; init; }
    public required string ActionName { get; init; }
    public string ActionText { get; set; }
}
new Toast
{
    ActionText = ""
}

Using required with set properties

Summary: Properties are required to be set during object initialization and can be mutated afterwords

The following should be valid because the "required" properties ActionId and ActionName are set during object initialization. Like the previous example, the programmer is forced to initialize the required properties which are normally set to default(T). The important bit in this example is that the properties are mutable. Note: It should also require value types to be initialized.

public record Toast
{
    public required int ActionId { get; set; }
    public required string ActionName { get; set; }
    public string ActionText { get; set; }
}
var toast = new Toast
{
    ActionId = 1,
    ActionName = ""
    ActionText = ""
}

toast.ActionId = 2;

The warning should show up on object initializers.

Because C# properties are not required anywhere today, this is not possible without the rest of the feature :).

AFAICT, other two sections are proposing a must modifier instead of the existing syntax proposal, is that correct?

@333fred

Because C# properties are not required anywhere today, this is not possible without the rest of the feature :).

This is true.

AFAICT, other two sections are proposing a must modifier instead of the existing syntax proposal, is that correct?

I use the must modifier in my proposal, but I don't have a preference on what modifier we use. If the existing syntax proposal uses something different, then we should use that instead. My goal was to describe the functionality I think this feature should have.

Did we settle on a specific modifier for this feature already? If we have I'll update my post to reflect the modifier we settled on.

Did we settle on a specific modifier for this feature already? If we have I'll update my post to reflect the modifier we settled on.

So far, we've been leaning towards required as a modifier for the property, not for the accessor. Specifically, remember that we want to be able to annotate fields with this as well, not just properties. That's why the checked-in proposal is named required-members.md, not required-properties.md.

My main question was, it didn't look like there was anything particularly different in your proposal from what is checked in, but I wanted to be sure I wasn't misreading anything you wrote.