Skip to content

Commit

Permalink
Add examples for nullability and null states (#1192)
Browse files Browse the repository at this point in the history
* first pass at null-state examples.

* build warning

* Fix test build issues

* Respond to initial feedback

* warnings, not errors

* one more error tester fix

* Apply suggestions from code review

* Apply suggestions from code review

Co-authored-by: Neal Gafter <[email protected]>

---------

Co-authored-by: Neal Gafter <[email protected]>
  • Loading branch information
BillWagner and gafter authored Nov 20, 2024
1 parent f378225 commit 2f9cbbc
Showing 1 changed file with 96 additions and 1 deletion.
97 changes: 96 additions & 1 deletion standard/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -863,8 +863,103 @@ The ***default null state*** of an expression is determined by its type, and the
A diagnostic can be produced when a variable ([§9.2.1](variables.md#921-general)) of a non-nullable reference type is initialized or assigned to an expression that is maybe null when that variable is declared in text where the annotation flag is enabled.
> *Example*: Consider the following method where a parameter is nullable and that value is assigned to a non-nullable type:
>
> <!-- Example: {template:"code-in-class-lib", name:"NullableAssignment", expectedWarnings:["CS8600"]} -->
> ```csharp
> #nullable enable
> public class C
> {
> public void M(string? p)
> {
> // Assignment of maybe null value to non-nullable variable
> string s = p;
> }
> }
> ```
>
> The compiler may issue a warning where the parameter that might be null is assigned to a variable that should not be null. If the parameter is null-checked before assignment, the compiler may use that in its nullable state analysis and not issue a warning:
>
> <!-- Example: {template:"code-in-class-lib", name:"NullChecked"} -->
> ```csharp
> #nullable enable
> public class C
> {
> public void M(string? p)
> {
> if (p != null)
> {
> string s = p;
> // Use s
> }
> }
> }
> ```
>
> *end example*
The compiler can update the null state of a variable as part of its analysis.
> *Note*: The compiler can treat a property ([§15.7](classes.md#157-properties)) as either a variable with state, or as independent get and set accessors ([§15.7.3](classes.md#1573-accessors)). In other words, a compiler can choose if writing to a property changes the null state of reading the property.
> *Example*: The compiler may choose to update the state based on any statements in your program:
>
> <!-- Example: {template:"code-in-class-lib", name:"UpdateStates", expectedWarnings:["CS8602","CS8602"]} -->
> ```csharp
> #nullable enable
> public void M(string? p)
> {
> // p is maybe-null
> int length = p.Length;
>
> // p is not null.
> string s = p;
>
> if (s != null)
> {
> int l2 = s.Length;
> }
> // s is maybe null
> int l3 = s.Length;
> }
> ```
>
> In the previous example, the compiler may decide that after the statement `int length = p.Length;`, the null-state of `p` is not-null. If it were null, that statement would have thrown a `NullReferenceException`. This is similar to the behavior if the code had been preceded by `if (p == null) throw NullReferenceException();` except that the code as written may produce a warning, the purpose of which is to warn that an exception may be thrown implicitly.
Later in the method, the code checks that `s` is not a null reference. The null-state of `s` can change to maybe null after the null-checked block closes. The compiler can infer that `s` is maybe null because the code was written to assume that it might have been null. Generally, when the code contains a null check, the compiler may infer that the value might have been null.*end example*
<!-- markdownlint-disable MD028 -->
<!-- markdownlint-enable MD028 -->
> *Example*: The compiler can treat a property ([§15.7](classes.md#157-properties)) as either a variable with state, or as independent get and set accessors ([§15.7.3](classes.md#1573-accessors)). In other words, a compiler can choose whether writing to a property changes the null state of reading the property, or if reading a property changes the null state of that property.
>
> <!-- Example: {template:"standalone-console", name:"NullPropertyAnalysis"} -->
> ```csharp
> class Test
> {
> private string? _field;
> public string? DisappearingProperty
> {
> get
> {
> string tmp = _field;
> _field = null;
> return tmp;
> }
> set
> {
> _field = value;
> }
> }
>
> static void Main()
> {
> var t = new Test();
> if (t.DisappearingProperty != null)
> {
> int len = t.DisappearingProperty.Length;
> }
> }
> }
> ```
>
> In the previous example, the backing field for the `DisappearingProperty` is set to null when it is read. However, a compiler may assume that reading a property doesn't change the null state of that expression. *end example*
***End of conditionally normative text***

0 comments on commit 2f9cbbc

Please sign in to comment.