Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add examples for nullability and null states #1192

Merged
merged 8 commits into from
Nov 20, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 -->

BillWagner marked this conversation as resolved.
Show resolved Hide resolved
<!-- 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*

BillWagner marked this conversation as resolved.
Show resolved Hide resolved
***End of conditionally normative text***
Loading