Open 333fred opened 4 years ago
I know naming is bikesheddy and not that valuable, but I do like required
(or reqd
if people think the full text is too long to keep typing out as often as it'll get used)
Is there much consensus yet on it using a new qualifier (must init foo
or reqd init foo
) vs a new property modifier (required foo
) ?
Nope nothing new in what I proposed. Thank you for informing me of the checked in proposals!
Is there much consensus yet on it using a new qualifier (
must init foo
orreqd init foo
) vs a new property modifier (required foo
) ?
Modifier, because we want to use it on fields as well.
Is there much consensus yet on it using a new qualifier (
must init foo
orreqd init foo
) vs a new property modifier (required foo
) ?Modifier, because we want to use it on fields as well.
Then I'd vote for required
spelled out too :)
I really love how C# is evolving but I don't like the idea of writing such long property definition just to make it required at the initialization. For me, less is more!
I agree with @DavidArno that the init
by itself should be enough to declare something required at initialization in conjunction with the nullable types. However, I understand that when not using nullable type checking (which is required by some people yet) that could be a problem. Thus, if we can't change the init
semantics, then I would go with that required
or must
modifiers.
I wish there were compiler options like in TypeScript where you could enable "strict" mode to enable these types of features instead of them being additional keywords.
We don't like dialects in the language :)
+1 for required
instead of must
, to avoid introducing synonyms. As in: "to make a property be required, add the keyword must
".
Is there any prior example of a C# keyword being a contraction or abbreviation? reqd
sounds like wrecked
. Typing required
will be just as easy since it should be req
+<space>
in the IDE. Seeing required
is probably also easier to read.
Is there any prior example of a C# keyword being a contraction or abbreviation?
reqd
sounds likewrecked
. Typingrequired
will be just as easy since it should bereq
+<space>
in the IDE. Seeingrequired
is probably also easier to read.
There's bool
for Boolean
but I personally don't like req
and don't see the harm in required
.
First of all, this keyword wouldn't be needed if nullable reference types were checked at compile time and giving errors instead of warnings if you didn't initialize non-nullable property. This really creates a lot of confusion about when to use nullable reference types and when to use required
for experienced C# developers and for new developers.
I think
public required string UserName { get; init; }
should be implicit when NRTs are enabled and you just write this:
public string UserName { get; init; }
All these features seem half-baked
Nullable and required are independent and orthogonal concerns.
It's valid to have a nullable required property that's explicitly initialised to null, the choice is the developers.
Nullable and required are independent and orthogonal concerns.
Yeah, I'm talking about non-nullable and required here because they seem the same. So why have both nullable reference types and required? Why can't I just enable nullable reference types and it would implicitly add required
when I don't add ?
? It's super confusing to me 🤷♂️
Consider I have <Nullable>enable</Nullable>
in my .csproj and I add a record like this:
public record User
{
public string UserName { get; init; }
}
So what difference will it make if I will add required
there instead? Logically it should be required
implicitly then. So now we have 4 ways of doing it:
public string UserName { get; init; }
public required string UserName { get; init; }
public required string? UserName { get; init; }
public string? UserName { get; init; }
And what the difference is between the first two? Non-nullable/nullable is synonymous with required/non-required to me.
Let me rephrase what I said.
Whether a property is nullable (or not) and whether it's required (or not) are independent concerns - they're orthogonal matters that have nothing to do with each other.
Looking at your examples:
// May optionally be initialized by the consumer, may not be set to null
public string UserName { get; init; }
// Must be set by the consumer, may not be set to null
public required string UserName { get; init; }
// Must be set by the consumer, may be explicitly set to null
public required string? UserName { get; init; }
// May optionally be initialized by the consumer to any value, including null
public string? UserName { get; init; }
For a more concrete example, consider this:
public required IRetryPolicy? RetryPolicy { get; init; }
I would write this if I wanted consuming code to be explicit about whether a retry policy was going to be used or not.
Seeing RetryPolicy = null
in the middle of object initialization eliminates any confusion on the part of a reader about what's going on.
I think their point for the first one
// May optionally be initialized by the consumer, may not be set to null
public string UserName { get; init; }
is that if it can't be set to null
, but it is optional, what value does it have if the consumer doesn't set it? The answer is... null
, which isn't allowed. I believe they're trying to say that in that scenario, the required
is implied.
But the null is not implied - the existing constructor of the class gets to run first, and it gets to set whatever IT wants:
public class Person
{
public string FullName { get; init; }
public string KnownAs { get; init; }
public string FamilyName { get; init; }
public class Person()
{
FullName = "";
KnownAs = "";
FamilyName = "";
}
}
Conflating nullability and required'ness together would lead to really odd situations - such as requiring all three of FullName
, KnownAs
and FamilyName
to be set every time, even though they already had valid non-null values.
I think I'm getting confused with some of the early talk about this feature. I thought that the compiler errors were going to show at the caller when the object, after completing construction, isn't "complete".
So, in your example above, no errors, as the default (and only) constructor deals with all the initialisation.
However, consider:
public class Person
{
public string FullName { get; init; }
public string KnownAs { get; init; }
public string FamilyName { get; init; }
public class Person()
{
FullName = "";
FamilyName = "";
}
}
var p = new Person();
That will create a scenario where KnownAs
is undefined. As it currently stands, you get a warning in the class. The required
tag moves that warning to the call-site (I thought). However, without the required
tag, the intention of the non-null nature of the property is that it is still required.
Don't get me wrong - I have no problem with typing required
to show further intent - I was just trying to see the issue from the direction of @bugproof
It's worth laying out the possibilities. In a nullable context this:
public class C
{
public string Prop { get; set; }
}
is no longer valid. Properties must either:
I don't necessarily agree that the last of those options should somehow be the default behaviour.
My understanding is that init
only means allowed to be set in the initialiser.
If your value was an 'int' then you would see different behaviours:
public int Age { get; init; }
- Will default to 0
if not set.
public required int Age { get; init; }
- Will raise the compiler warning at the caller.
public int? Age{ get; init; }
- Will default to null
if not set.
The only way to switch the compiler error on for an int
is therefore to use an extra flag required
to differentiate between behaviour.
I guess there is an argument that it could be handled by no longer defaulting the int
to 0 unless it was explicitly set using public int Age { get; init; } = 0
, but personally I feel that is changing currently expected behaviour of the primatives.
And that is the killer piece of data I was forgetting. It can be implied (to a large degree) for reference types, but not value types.
There you go, @bugproof - there's your answer as to why required
is, well, required...
One thing I haven't seen addressed here is how required initialization will interact with the new()
constraint.
Will this be an error?
class C<T> where T : new()
{
}
record R
{
public required string S { get; init; }
}
class Program
{
public static void Main() { new C<R>(); }
}
The proposal specification includes this note at the end:
In order to ensure that we have design space in this area, we forbid
required
in interfaces, and forbid types with _required_memberlists from being substituted for type parameters constrained tonew()
.
Which suggests that yes, that would be an error
I missed that part. Good to know, thanks!
Maybe if required
keyword will be a thing then the compiler could allow something like this?
record Person(string Name);
// would be also possible to do
var person = new Person { Name = "Indiana Jones" };
// instead of
var person = new Person("Indiana Jones");
i.e. it would be possible to use parameterless constructor see https://github.com/dotnet/csharplang/discussions/4842
This would also be closer to how F# allows you to define records...
type Person = {Name: string}
We're not planning on having required changing anything about adding new constructors.
I think
public required string UserName { get; init; }
should be implicit when NRTs are enabled and you just write this:
public string UserName { get; init; }
I really agree with this
I think
public required string UserName { get; init; }
should be implicit when NRTs are enabled and you just write this:public string UserName { get; init; }
I really agree with this
I think the “nullability and requiredness are orthogonal” position is unfortunate. It creates this situation where you have to keep piling on modifiers to get pit of success behaviour instead of the pit of failure.
I think
public required string UserName { get; init; }
should be implicit when NRTs are enabled and you just write this:public string UserName { get; init; }
I really agree with this
I think the “nullability and requiredness are orthogonal” position is unfortunate. It creates this situation where you have to keep piling on modifiers to get pit of success behaviour instead of the pit of failure.
My only concern would be that it would then mean an inconsistency between ref and value types Slightly forced example of both of these in the same class:
public string UserName { get; init; }
public required int Age { get; init; }
The value type needs it specified as otherwise the default of 0 is valid (which it might be in other use cases, just not for age).
To me that looks slightly odd as both are required
it is just that the ref string
one is implicit if NRT is enabled.
Also while I would love for NRT to be the default behaviour, it isn't (yet???), and so looking at this class I would have to go the csproj to check if it was turned on before knowing whether username is required or not. As someone who jumps about projects that are in different versions of .net etc. reviewing a PR to catch that the required
is missing because the project doesn't have NRT enabled would be a real pain.
My only concern would be that it would then mean an inconsistency between ref and value types
I know this doesn't justify everything but NRT and NVT are already vastly different beasts. They have all kind of discrepancies that C# justifies because of their different runtime representation. To me the inconsistency here is somehow "consistent" with existing inconsistencies.
In fact, you say you would find odd that one needs required
and the other does not, but with no required
modifiers do you find odd that one raises a C# warning and the other doesn't?
The inconsistency is already there.
I agree with previous comments that making uninitialized NRT required by default is more ergonomic than producing errors.
@bugproof
Was already asked here: #4857
My vote is a keyword modifier on
set
/init
. I kind of likerequired
althoughreq
andmust
are ok, too.mustinit
seems redundant.public string Foo { get; required set; } public string Bar { get; required init; }
What would required set
be for?
My vote is a keyword modifier on
set
/init
. I kind of likerequired
althoughreq
andmust
are ok, too.mustinit
seems redundant.public string Foo { get; required set; } public string Bar { get; required init; }
What would
required set
be for?
It must be set during initialisation, but can be set thereafter, presumably.
I think it has to go on the property itself, as required init;
implies that it is required that an init block sets the property.
If the property is get in a constructor, it still meets the required
status, but not in an init block
I don't see that at all. I'd expect required
to mean it must be set during construction or initialisation. So if I call a constructor that sets it, then I do not need to set it in an initialisation block.
But having said that required get
makes no sense, so public required int Age { get; init; }
and
public int Age { get; required init; }
should be semantically identical and therefore where the required
goes is really not important.
I am of the opinion that required should go out front (toward the left) because it is a major aspect to a property and I believe having it left makes it more readily apparent.
But I am sure someone on here will state just the opposite. :-)
TLDR: why can't required be default for all properties unless they have nullable type (e.g. int?
or string?
) or explicit =default
if I want something to be default I don't see a problem explicitly adding = default
Also it's kinda funny how I get warnings that NRT is not initialized anywhere but I don't get that warnings for int
when it's not int?
Obviously to get rid of that warning I'd have to initialize it from constructor or suppress the warning with null forgiving operator = null!
but I couldn't guarantee that it will be assigned by the person using the record/class.
When there are many properties like 10 I'd have to initialize them all. required
will help with this, but I still kinda wonder why it's not the default behaviour when something is not nullable... I think there should be a compiler option to make required
implicit for all non nullable types (types without ?
postfix).
When I write int
or string
I obviously don't want it to be null nor implicitly default when creating the object either by constructor or initialization syntax. I think when I want something to be default it should be explicitly specified.
Why force us to write public required string Name { get; init; }
when NRTs are enabled? Same goes for ints... If the type is int
and not int?
I want it to be explicitly initialized most of the time...
Logically thinking this is how it should look like:
int
- implicitly required unless you set it to = default
int?
- not required
string
- implicitly required
string?
- not required
I really don't want to write
required
everytime when the type alone says if it is required or not
This is how I see required
keyword to be honest:
So now when I enable NRT
and type public string Name { get; init; }
I will still get a warning that I have to initialize it from constructor or explicitly add required
.
@bugproof
TLDR: why can't required be default for all properties unless they have nullable type
Because that would break an enormous amount of code and would most likely not what the author of that code intended.
Also it's kinda funny how I get warnings that NRT is not initialized anywhere but I don't get that warnings for
int
when it's notint?
Because the implicit default value of int
doesn't lead to NullReferenceException
. Again, such a change would break an enormous amount of existing code.
but I still kinda wonder why it's not the default behaviour when something is not nullable...
As stated several times required
properties have nothing to do with avoiding null or default values for those properties. You can explicitly set a required
property to default
(or null
). A nullable property can have a default non-null value. These are orthogonal concerns.
If your intent is to require that the consumer of your class set a property during initialization then that is what required
is for. That is not dependent on the type of the property nor can it be made the default behavior in arbitrary circumstances without causing breaking changes and resulting in developers who don't want that property to be required lacking a means to communicate that without another language feature.
@HaloFour
Because that would break an enormous amount of code and would most likely not what the author of that code intended.
It wouldn't if it could be optionally enabled like NRTs. Some .csproj tag like <ImplicitRequired>enable</ImplicitRequired>
and everyone would be happy. The resulting bytecode would be the same anyway. It would only allow you to prevent mistakes in your own code. It wouldn't have any impact on existing code. If code generators could do it I would happily write such generator myself.
Some .csproj tag like
<ImplicitRequired>enable</ImplicitRequired>
and everyone would be happy.
Because that would mean that a compiler flag would modify the meaning of your code, which the language team are explicit about not wanting to do.
(and before you say it, NRT does not modify the meaning of your code - it enables you to write code you couldn't without it, just like unsafe)
Because that would mean that a compiler flag would modify the meaning of your code, which the language team are explicit about not wanting to do.
Why not? It would only make things better and less error prone. If it's not possible then maybe it's possible to write some roslyn analyzer that errors when you don't assign everything that's non nullable?
@bugproof
If it's not possible then maybe it's possible to write some roslyn analyzer that errors when you don't assign everything that's non nullable?
You're free to do that.
Why not? It would only make things better and less error prone.
Quite the opposite - because this compiler flag could be in a CLI switch instead of a .csproj file, a person who has your code doesn't know whether it should be compiled with the flag or without it, and the final program would be different depending on which option you choose. And that will create more errors than fix, in my mind.
You would have to ask yourself whether you would want to enforce this:
public class C
{
public bool B { get; init; } = false;
}
...or if this is also okay:
public class C
{
public bool B { get; init; }
public C()
{
B = false;
}
}
If you want only the former to be allowed, it's easy enough. If you want to allow the second one as well it gets much thornier. Here's an old Eric Lippert post about why the compiler doesn't enforce initialization: https://stackoverflow.com/a/30820127/1696533
Just a thought: How about required class
and required record
to make all properties required for that class/record?
Just a thought: How about
required class
andrequired record
to make all properties required for that class/record?
I like it. required sealed record Foo
is still a longwinded way of getting to a "best practice" starting point for a new type, but it's better than having to put required
against every parameter.
Just a thought: How about
required class
andrequired record
to make all properties required for that class/record?
And what if property was nullable? Would it also be required or you would be forced to assign null to every property like that?
Just a thought: How about
required class
andrequired record
to make all properties required for that class/record?And what if property was nullable? Would it also be required or you would be forced to assign null to every property like that?
You'd be forced to assign a value. It just so happens null is a valid option.
@bugproof, I agree with @KieranDevvs here. Whether it's nullable or not is irrelevant if you mark it required
, Mark it thus and you are then required to specify a value, even if that value is null
. Marking it null-nullable implies it's required and you'll get a warning that it needs defaulting if you do not mark it required. I don't see the opposite (nullable and required) as an issue.
Required Properties
Specification: https://github.com/dotnet/csharplang/blob/master/proposals/csharp-11.0/required-members.md
Design meetings
https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-09-16.md#required-properties https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-12-07.md https://github.com/dotnet/csharplang/blob/master/meetings/2021/LDM-2021-01-11.md https://github.com/dotnet/csharplang/blob/master/meetings/2021/LDM-2021-03-03.md#required-members https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-10-25.md#required-members https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-12-15.md#required-parsing https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-03-21.md#open-question-in-required-members https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-03-23.md#open-questions-in-required-members https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-05-02.md#effect-of-setsrequiredmembers-on-nullable-analysis https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-11-30.md#revise-membernotnull-for-required