csharplang/meetings/2022/LDM-2022-05-02.md

5.4 KiB

C# Language Design Meeting for May 2nd, 2022

Agenda

Quote of the Day

  • "I think I've used goto in C# more than I've used a virtual auto property"

Discussion

Effect of SetsRequiredMembers on nullable analysis

https://github.com/dotnet/csharplang/issues/3630
https://github.com/dotnet/csharplang/issues/6081

We have an open question on required properties: what should the effect of SetsRequiredMembers be on nullable analysis? Should the property be taken as a tool to silence the compiler, or should it restore the nullable warning to the current constructor?

A key insight of required as a feature is that is orthogonal to nullable. It can move where a nullable warning is reported, but it does not suppress the warning: assigning a not-null reference type property to null will still produce a warning. If SetsRequiredMembers started actually suppressing the nullable warning, that would mean that this orthogonality is lost, and the two features become more entangled that we would otherwise want. While there could be some users confused why we aren't also warning or erroring on nullable or value type members that aren't set in the constructor, we think the right decision is to have SetsRequiredMembers move the nullable warning back to the constructor, rather than suppressing it completely.

Conclusion

Nullable warning will be moved back to the constructor when SetsRequiredMembers is applied.

field questions

https://github.com/dotnet/csharplang/issues/140

Partial overrides of virtual properties

https://github.com/dotnet/csharplang/issues/6076
https://github.com/dotnet/csharplang/issues/6077

First up in semi-auto properties, we have a couple of questions about the behavior of overriding a virtual property with a semi-auto property. There existing rules in C# that prevent an auto-property from overriding only one accessor of a virtual property, as it's a potential footgun in waiting. There are two possible viewpoints here:

  1. This is a restriction that applies to properties with generated fields. This property has a generated field, so the restriction should apply, as the footgun is still there.
  2. This restriction doesn't apply to properties with manually-implemented bodies, so it shouldn't apply here either.

After some discussion, we're concerned that the footgun the rule exists to prevent is still here for semi-auto properties. We're also concerned about what the meaning of this code is:

class Base
{
    public virtual int Prop { get; set; }
}
class Derived : Base
{
    public override int Prop { get => field; }

    public Derived()
    {
        Prop = 1; // Does this call the setter from Base? Or does it assign to the backing field in Derived?
    }
}

We don't have a good answer for what this code does: by the specification, it should likely assign to the backing field. But either behavior is surprising.

We also had a small side conversation on whether we should have done work with auto properties when they were first introduced to allow them to share storage across virtual hierarchies, but there are issues with this. If this could be done across assembly boundaries, it would be exposing that you have an auto-property as a part of the public contract of a type, and that breaks the encapsulation that properties provide.

Conclusion

Semi-auto properties must override all accessors of a base property.

Definite assignment of manually implemented setters

https://github.com/dotnet/csharplang/issues/6080

In C# 11, we are changing structs to automatically default initialize fields if the struct is read before all fields are assigned, and we need to decide if this behavior should apply to semi-auto properties as well. Our options are:

  1. Do not default initialize the struct if a manually-implemented semi-auto property setter is called. This could either be a blanket statement, or we could try to do some cross-method analysis to determine if we need to do the default initialization of the property.
  2. Default initialize as if it was a regular property setter.
  3. Default initialize as if it was a regular property setter and suppress the warning, as there's nothing the user can do about it.

For option 1, we don't think this is the right time to introduce cross-method analysis into C#. It would bring with it all the complexity of cross-method analysis for very little benefit; if we were going to introduce cross-method analysis, there are areas such as nullable analysis that would have the exact same set of problems, but would be much more beneficial. For option 3, we note that the uninitialized struct warning is not turned on by default, and users have to opt into it. For these users, there is indeed nothing that can be done about the warning except providing an explicit property initializer, or not using the feature. Most C# users will never see the warning, and for those that do care, they should be aware of it in this case too.

Conclusion

Default initialize when calling a manually implemented semi-auto property setter, and issue a warning when doing so, like a regular property setter.