csharplang/meetings/2017/LDM-2017-11-27.md
2017-12-20 17:05:17 -08:00

3.7 KiB

C# Language Design Notes for Nov 27, 2017

Agenda

We went over the feedback on the nullable reference types prototype, and discussed how to address the top issues that people had found using the feature on their own source code.

  1. Interacting with existing, unannotated APIs
  2. Accommodating alternative initialization patterns
  3. Tracking nullable value types
  4. Tracking dotted names
  5. Special methods
  6. Filtering out nulls

Interacting with existing, unannotated APIs

The most pressing problem for people using the prototype, is when existing, unannotated APIs are treated as all non-nullable. While you can live with that, there ends up being too many places where you have to use ! to silence warnings.

Long term, of course the solution is for these APIs to evolve and to get annotations. That presents its own challenges: do people have to wait for them to update in place? Can we have a system of on-the-side annotations, either through reference assemblies or otherwise?

Short term, though, it seems that we should probably distinguish "legacy" APIs, and simply not warn based on their signatures. The way to recognize them is to bake an attribute into new assemblies - then old ones are simply the ones without that attribute.

There's design work to decide what exactly it means to "not warn on legacy signatures": do they represent a third type state ("between" nullable and non-nullable)? Does it travel with type inference? Etc.

Accommodating alternative initialization patterns

The prototype warns when constructors do not initialize all non-nullable fields. However, this is too harsh for many usage patterns, where fields may be initialized by:

  • Initialization helpers called from constructors
  • Factory methods that call constructors
  • Object initializers, by convention
  • Set-up/tear-down methods in test frameworks
  • Reflection

We can try to do something more fancy to track initialization of fields through at least some of these. At the end of the day, there will be initialization that we just don't recognize, so there should also be a way to opt out of these warnings.

Tracking dotted names

The prototype ended up not supporting the tracking of null-state for dotted names, and that was definitely felt by several prototype users, which goes to show that we do indeed need this functionality, as we suspected.

Tracking nullable value types

There's some desire to have the same tracking of null-state for nullable value types. Not only could we allow you to dot through to the members of the underlying type (with some finagling to avoid breaking changes), but this would also be helpful when boxing the nullable value type.

Special methods

Certain scenarios came up again and again, where utility methods or specific method patterns have special behavior regarding null. We need to design a general approach to this, where certain attributes on these methods can change their nullability behavior. Examples:

  • String.IsNullOrEmpty(s): s is not-null when method returns false
  • TryGet(out T x): x may-be-null when method returns false (even if T is non-nullable)
  • FirstOrDefault(): result may-be-null (even if element type is non-nullable)

There's design work needed.

Filtering out nulls

In this query it would be really good to know that the result is of non-null element type:

var query =
    from s in nullableStrings
    where s != null
    select s;

For query expressions we can maybe deal with this in the language. For the method syntax, it does not seem viable:

var query =
    nullableStrings.Where(s => s != null);

How would we know that the result is filtered by the lambda provided?

It's more likely that we can make a specialized WhereNotNull query method for this purpose.