可為 null 的參考型別規格Nullable Reference Types Specification

這是進行中的工作,有幾個部分遺失或不完整。This is a work in progress - several parts are missing or incomplete.

這項功能會將兩種新類型的可為 null 的型別新增 (可為 null 的參考型別,並將可為 null 的泛型型別) 至現有可為 null 的實值型別,並且導入靜態流程分析以This feature adds two new kinds of nullable types (nullable reference types and nullable generic types) to the existing nullable value types, and introduces a static flow analysis for purpose of null-safety.

SyntaxSyntax

可為 null 的參考型別及可為 null 的型別參數Nullable reference types and nullable type parameters

可為 null 的參考型別與可為 null 的型別參數具有與可為 null 實值型別的簡短形式相同的語法 T? ,但是沒有對應的完整格式。Nullable reference types and nullable type parameters have the same syntax T? as the short form of nullable value types, but do not have a corresponding long form.

基於規格的目的,目前的 nullable_type 生產會重新命名為 nullable_value_type ,而且 nullable_reference_type nullable_type_parameter 會加入生產環境:For the purposes of the specification, the current nullable_type production is renamed to nullable_value_type, and nullable_reference_type and nullable_type_parameter productions are added:

type
    : value_type
    | reference_type
    | nullable_type_parameter
    | type_parameter
    | type_unsafe
    ;

reference_type
    : ...
    | nullable_reference_type
    ;

nullable_reference_type
    : non_nullable_reference_type '?'
    ;

non_nullable_reference_type
    : reference_type
    ;

nullable_type_parameter
    : non_nullable_non_value_type_parameter '?'
    ;

non_nullable_non_value_type_parameter
    : type_parameter
    ;

non_nullable_reference_type中的 nullable_reference_type 必須是 (類別、介面、委派或陣列) 的不可為 null 參考型別。The non_nullable_reference_type in a nullable_reference_type must be a nonnullable reference type (class, interface, delegate or array).

non_nullable_non_value_type_parameterIn nullable_type_parameter 必須是不受限於實值型別的型別參數。The non_nullable_non_value_type_parameter in nullable_type_parameter must be a type parameter that isn't constrained to be a value type.

可為 null 的參考型別及可為 null 的類型參數不能出現在下列位置:Nullable reference types and nullable type parameters cannot occur in the following positions:

  • 作為基類或介面as a base class or interface
  • 作為的接收者 member_accessas the receiver of a member_access
  • 作為 type``object_creation_expressionas the type in an object_creation_expression
  • 作為 delegate_type``delegate_creation_expressionas the delegate_type in a delegate_creation_expression
  • 做為 type is_expressioncatch_clausetype_patternas the type in an is_expression, a catch_clause or a type_pattern
  • 作為 interface 完整介面成員名稱中的。as the interface in a fully qualified interface member name

nullable_reference_type 和中,會針對 nullable_type_parameter 停用 的可為 null 注釋內容提供警告。A warning is given on a nullable_reference_type and nullable_type_parameter in a disabled nullable annotation context.

class and class? 條件約束class and class? constraint

class條件約束有一個可為 null 的對應 class?The class constraint has a nullable counterpart class?:

primary_constraint
    : ...
    | 'class' '?'
    ;

在啟用的注釋內容中,以 (條件約束的類型參數 class) 必須使用不可為 null 的參考型別具現化。 A type parameter constrained with class (in an enabled annotation context) must be instantiated with a nonnullable reference type.

class? (或 class已停用 的注釋內容中受條件約束的類型參數,) 可能會以可為 null 或不可為 null 的參考型別來具現化。A type parameter constrained with class? (or class in a disabled annotation context) may either be instantiated with a nullable or nonnullable reference type.

class?已停用 批註內容中的條件約束會提供警告。A warning is given on a class? constraint in a disabled annotation context.

notnull 約束notnull constraint

受條件約束的類型參數不可以是可為 null 的型別 (可為 null 的實 notnull 值型別、可為 null 的參考型別或可為) nullA type parameter constrained with notnull may not be a nullable type (nullable value type, nullable reference type or nullable type parameter).

primary_constraint
    : ...
    | 'notnull'
    ;

default 約束default constraint

default條件約束可用於方法覆寫或明確的執行,以 T? 區別「可為 null 的型別參數」與「可為 null 的實值型別」 () 的意義 Nullable<T>The default constraint can be used on a method override or explicit implementation to disambiguate T? meaning "nullable type parameter" from "nullable value type" (Nullable<T>). 如果缺少 default 條件約束,則會將覆 T? 寫或明確執行中的語法解釋為 Nullable<T>Lacking the default constraint a T? syntax in an override or explicit implementation will be interpreted as Nullable<T>

請參閱https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/unconstrained-type-parameter-annotations.md#default-constraintSee https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/unconstrained-type-parameter-annotations.md#default-constraint

Null 容許運算子The null-forgiving operator

修正後的 ! 運算子稱為 null 容許運算子。The post-fix ! operator is called the null-forgiving operator. 它可以套用在 primary_expressionnull_conditional_expression 內:It can be applied on a primary_expression or within a null_conditional_expression:

primary_expression
    : ...
    | null_forgiving_expression
    ;

null_forgiving_expression
    : primary_expression '!'
    ;

null_conditional_expression
    : primary_expression null_conditional_operations_no_suppression suppression?
    ;

null_conditional_operations_no_suppression
    : null_conditional_operations? '?' '.' identifier type_argument_list?
    | null_conditional_operations? '?' '[' argument_list ']'
    | null_conditional_operations '.' identifier type_argument_list?
    | null_conditional_operations '[' argument_list ']'
    | null_conditional_operations '(' argument_list? ')'
    ;

null_conditional_operations
    : null_conditional_operations_no_suppression suppression?
    ;

suppression
    : '!'
    ;

例如:For example:

var v = expr!;
expr!.M();
_ = a?.b!.c;

primary_expressionnull_conditional_operations_no_suppression 必須是可為 null 的型別。The primary_expression and null_conditional_operations_no_suppression must be of a nullable type.

後置 ! 運算子沒有執行時間效果-它會評估為基礎運算式的結果。The postfix ! operator has no runtime effect - it evaluates to the result of the underlying expression. 唯一的角色是將運算式的 null 狀態變更為「not null」,並限制其使用所提供的警告。Its only role is to change the null state of the expression to "not null", and to limit warnings given on its use.

可為 null 的編譯器指示詞Nullable compiler directives

#nullable 指示詞會控制可為 null 的注釋和警告內容。#nullable directives control the nullable annotation and warning contexts.

pp_directive
    : ...
    | pp_nullable
    ;

pp_nullable
    : whitespace? '#' whitespace? 'nullable' whitespace nullable_action (whitespace nullable_target)? pp_new_line
    ;

nullable_action
    : 'disable'
    | 'enable'
    | 'restore'
    ;

nullable_target
    : 'warnings'
    | 'annotations'
    ;

#pragma warning 指示詞會展開以允許變更可為 null 的警告內容:#pragma warning directives are expanded to allow changing the nullable warning context:

pragma_warning_body
    : ...
    | 'warning' whitespace warning_action whitespace 'nullable'
    ;

例如:For example:

#pragma warning disable nullable

可為 Null 內容Nullable contexts

每一行原始程式碼都有一個 可為 null 的注釋內容可為 null 的警告內容Every line of source code has a nullable annotation context and a nullable warning context. 這些會控制可為 null 的注釋是否有效果,以及是否有指定的可 null 性警告。These control whether nullable annotations have effect, and whether nullability warnings are given. 指定行的注釋內容已 停用啟用The annotation context of a given line is either disabled or enabled. 指定行的警告內容已 停用啟用The warning context of a given line is either disabled or enabled.

這兩個內容可以在專案層級的 (指定于 c # 原始程式碼) ,或透過預先處理器指示詞在來源檔案中的任何位置 #nullableBoth contexts can be specified at the project level (outside of C# source code), or anywhere within a source file via #nullable pre-processor directives. 如果未提供任何專案層級設定,則預設值為 停用 兩個內容。If no project level settings are provided the default is for both contexts to be disabled.

指示詞 #nullable 會控制來源文字內的注釋和警告內容,並優先于專案層級的設定。The #nullable directive controls the annotation and warning contexts within the source text, and take precedence over the project-level settings.

指示詞會設定 (s 的內容,) 它控制後續程式程式碼,直到另一個指示詞覆寫它,或直到原始程式檔結束為止。A directive sets the context(s) it controls for subsequent lines of code, until another directive overrides it, or until the end of the source file.

指示詞的效果如下所示:The effect of the directives is as follows:

  • #nullable disable:將可為 null 的注釋和警告內容設定為 停用#nullable disable: Sets the nullable annotation and warning contexts to disabled
  • #nullable enable:將可為 null 的注釋和警告內容設定為 已啟用#nullable enable: Sets the nullable annotation and warning contexts to enabled
  • #nullable restore:將可為 null 的注釋和警告內容還原至專案設定#nullable restore: Restores the nullable annotation and warning contexts to project settings
  • #nullable disable annotations:將可為 null 注釋內容設定為 停用#nullable disable annotations: Sets the nullable annotation context to disabled
  • #nullable enable annotations:將可為 null 注釋內容設定為 已啟用#nullable enable annotations: Sets the nullable annotation context to enabled
  • #nullable restore annotations:將可為 null 注釋內容還原至專案設定#nullable restore annotations: Restores the nullable annotation context to project settings
  • #nullable disable warnings:將可為 null 的警告內容設定為 停用#nullable disable warnings: Sets the nullable warning context to disabled
  • #nullable enable warnings:將可為 null 的警告內容設定為 已啟用#nullable enable warnings: Sets the nullable warning context to enabled
  • #nullable restore warnings:將可為 null 警告內容還原至專案設定#nullable restore warnings: Restores the nullable warning context to project settings

型別的可 NULL 性Nullability of types

給定的型別可以有三個時機的其中一個: 無警示不可為 nullnullableA given type can have one of three nullabilities: oblivious, nonnullable, and nullable.

如果有可能的值指派給 不可為 null 類型,則可能會造成警告 nullNonnullable types may cause warnings if a potential null value is assigned to them. 不過,無警示可為 null 的型 別為「null 可指派」,而且可以有 null 指派給它們的值而不發出警告。Oblivious and nullable types, however, are "null-assignable" and can have null values assigned to them without warnings.

無警示不可為 null 類型的值可以在沒有警告的情況下取值或指派。Values of oblivious and nonnullable types can be dereferenced or assigned without warnings. 不過, 可為 null 的型別值為「null 產生」,而且可能會在未適當的 null 檢查的情況下進行取值或指派時產生警告。Values of nullable types, however, are "null-yielding" and may cause warnings when dereferenced or assigned without proper null checking.

Null 產生型別的 預設 null 狀態 是「可能是 null」或「可能是預設值」。The default null state of a null-yielding type is "maybe null" or "maybe default". 非 null 產生型別的預設 null 狀態為 "not null"。The default null state of a non-null-yielding type is "not null".

型別的型別和它在中的可為 null 注釋內容會決定其 null 屬性:The kind of type and the nullable annotation context it occurs in determine its nullability:

  • 不可為 null 數值型別 S 一律為 不可為 nullA nonnullable value type S is always nonnullable
  • 可為 null 的實值型別 S? 一律為 nullA nullable value type S? is always nullable
  • C已停用 注釋內容中的 unannotated 參考型別為 無警示An unannotated reference type C in a disabled annotation context is oblivious
  • 啟用的注釋內容中的 unannotated 參考型別 C不可為 null An unannotated reference type C in an enabled annotation context is nonnullable
  • 可為 null 的參考型別可為 C? null (但 停用 的注釋內容中可能產生警告) A nullable reference type C? is nullable (but a warning may be yielded in a disabled annotation context)

類型參數會額外將其條件約束納入考慮:Type parameters additionally take their constraints into account:

  • T如果有任何) 是可為 null 的類型或 class? 條件約束 可為 null ,則為所有條件約束 (的類型參數A type parameter T where all constraints (if any) are either nullable types or the class? constraint is nullable
  • 類型參數, T 其中至少有一個條件約束是 無警示不可為 null ,或是或或其中一個 struct class notnull 條件約束為A type parameter T where at least one constraint is either oblivious or nonnullable or one of the struct or class or notnull constraints is
    • 停用 的注釋內容中 無警示oblivious in a disabled annotation context
    • 已啟用 的注釋內容中 不可為 nullnonnullable in an enabled annotation context
  • 可為 null 的型別參數可為 T? null,但如果不是實數值型別,則會在 已停用 的注釋內容中產生警告 TA nullable type parameter T? is nullable, but a warning is yielded in a disabled annotation context if T isn't a value type

無警示與不可為 nullOblivious vs nonnullable

type當類型的最後一個標記是在該內容中時,會被視為在指定的注釋內容中發生。A type is deemed to occur in a given annotation context when the last token of the type is within that context.

在原始程式碼中指定的參考型別是否 C 會被解釋為無警示或不可為 null,取決於該原始程式碼的注釋內容。Whether a given reference type C in source code is interpreted as oblivious or nonnullable depends on the annotation context of that source code. 但是一旦建立之後,它就會被視為該型別的一部分,而且會在替代泛型型別引數時,以「傳送」。But once established, it is considered part of that type, and "travels with it" e.g. during substitution of generic type arguments. 如同型別上有像這樣的注釋 ? ,但是看不見。It is as if there is an annotation like ? on the type, but invisible.

條件約束Constraints

可為 null 的參考型別可以當做泛型條件約束使用。Nullable reference types can be used as generic constraints.

class? 這是表示「可能可為 null 的參考型別」的新條件約束,而 class 啟用 的注釋內容表示「不可為 null 參考型別」。class? is a new constraint denoting "possibly nullable reference type", whereas class in an enabled annotation context denotes "nonnullable reference type".

default 這是新的條件約束,表示不知道為參考或實數值型別的型別參數。default is a new constraint denoting a type parameter that isn't known to be a reference or value type. 它只能用於覆寫的和明確實作為方法。It can only be used on overridden and explicitly implemented methods. 使用這個條件約束時, T? 表示可為 null 的型別參數,而不是的縮寫 Nullable<T>With this constraint, T? means a nullable type parameter, as opposed to being a shorthand for Nullable<T>.

notnull 這是新的條件約束,表示不可為 null 的型別參數。notnull is a new constraint denoting a type parameter that is nonnullable.

型別引數或條件約束的 null 屬性不會影響型別是否符合條件約束,但目前 (可為 null 的實值型別不符合 struct 條件約束) 的情況除外。The nullability of a type argument or of a constraint does not impact whether the type satisfies the constraint, except where that is already the case today (nullable value types do not satisfy the struct constraint). 但是,如果類型引數不符合條件約束的 null 屬性需求,則可能會提供警告。However, if the type argument does not satisfy the nullability requirements of the constraint, a warning may be given.

Null 狀態和 null 追蹤Null state and null tracking

給定來源位置中的每個運算式都有 null 狀態,指出是否認為可能會評估為 null。Every expression in a given source location has a null state, which indicated whether it is believed to potentially evaluate to null. Null 狀態可能是「非 null」、「可能是 null」或「可能是預設值」。The null state is either "not null", "maybe null", or "maybe default". Null 狀態是用來判斷是否應該針對 null 不安全的轉換和取值來指定警告。The null state is used to determine whether a warning should be given about null-unsafe conversions and dereferences.

「可能的 null」和「可能的預設值」之間的區別很微妙,而且適用于型別參數。The distinction between "maybe null" and "maybe default" is subtle and applies to type parameters. 差別在於具有「可能為 null」狀態的型別參數 T 表示值是在合法值的定義域中 T ,但合法值可能會包含在內 nullThe distinction is that a type parameter T which has the state "maybe null" means the value is in the domain of legal values for T however that legal value may include null. 在「可能的預設值」中,表示值可能超出的值合法網域 TWhere as a "maybe default" means that the value may be outside the legal domain of values for T.

範例:Example:

// The value `t` here has the state "maybe null". It's possible for `T` to be instantiated
// with `string?` in which case `null` would be within the domain of legal values here. The 
// assumption though is the value provided here is within the legal values of `T`. Hence 
// if `T` is `string` then `null` will not be a value, just as we assume that `null` is not
// provided for a normal `string` parameter
void M<T>(T t)
{
    // There is no guarantee that default(T) is within the legal values for T hence the 
    // state *must* be "maybe-default" and hence `local` must be `T?`
    T? local = default(T);
}

變數的 Null 追蹤Null tracking for variables

針對某些表示變數、欄位或屬性的運算式,會根據對其指派、在其上執行的測試,以及兩者之間的控制流程,在發生次數之間追蹤 null 狀態。For certain expressions denoting variables, fields or properties, the null state is tracked between occurrences, based on assignments to them, tests performed on them and the control flow between them. 這類似于為變數追蹤明確指派的方式。This is similar to how definite assignment is tracked for variables. 追蹤的運算式是下列格式的運算式:The tracked expressions are the ones of the following form:

tracked_expression
    : simple_name
    | this
    | base
    | tracked_expression '.' identifier
    ;

其中識別碼代表欄位或屬性。Where the identifiers denote fields or properties.

在無法連線的程式碼中,追蹤變數的 null 狀態為 "not null"。The null state for tracked variables is "not null" in unreachable code. 這會遵循其他有關無法存取的程式碼的決策,例如考慮是否要明確指派所有的區域變數。This follows other decisions around unreachable code like considering all locals to be definitely assigned.

描述類似于明確指派的 null 狀態轉換Describe null state transitions similar to definite assignment

運算式的 Null 狀態Null state for expressions

運算式的 null 狀態衍生自其表單和類型,以及其相關變數的 null 狀態。The null state of an expression is derived from its form and type, and from the null state of variables involved in it.

常值Literals

常值的 null 狀態 null 取決於運算式的目標型別。The null state of a null literal depends on the target type of the expression. 如果目標型別是限制為參考型別的類型參數,則它是「可能是預設值」。If the target type is a type parameter constrained to a reference type then it's "maybe default". 否則,它會是「可能為 null」。Otherwise it is "maybe null".

常值的 null 狀態 default 取決於常值的目標型別 defaultThe null state of a default literal depends on the target type of the default literal. default具有目標型別的常 T 值與運算式具有相同的 null 狀態 default(T)A default literal with target type T has the same null state as the default(T) expression.

任何其他常值的 null 狀態為 "not null"。The null state of any other literal is "not null".

簡單名稱Simple names

如果 simple_name 未分類為值,其 null 狀態為 "not null"。If a simple_name is not classified as a value, its null state is "not null". 否則,它是追蹤的運算式,而且其 null 狀態為其在此來源位置的追蹤 null 狀態。Otherwise it is a tracked expression, and its null state is its tracked null state at this source location.

成員存取Member access

如果 member_access 未分類為值,其 null 狀態為 "not null"。If a member_access is not classified as a value, its null state is "not null". 否則,如果它是追蹤的運算式,其 null 狀態就是其在此來源位置的追蹤 null 狀態。Otherwise, if it is a tracked expression, its null state is its tracked null state at this source location. 否則其 null 狀態為其類型的預設 null 狀態。Otherwise its null state is the default null state of its type.

var person = new Person();

// The receiver is a tracked expression hence the member_access of the property 
// is tracked as well 
if (person.FirstName is not null)
{
    Use(person.FirstName);
}

// The return of an invocation is not a tracked expression hence the member_access
// of the return is also not tracked
if (GetAnonymous().FirstName is not null)
{
    // Warning: Cannot convert null literal to non-nullable reference type.
    Use(GetAnonymous().FirstName);
}

void Use(string s) 
{ 
    // ...
}

public class Person
{
    public string? FirstName { get; set; }
    public string? LastName { get; set; }

    private static Person s_anonymous = new Person();
    public static Person GetAnonymous() => s_anonymous;
}

叫用運算式Invocation expressions

如果叫 invocation_expression 用使用一或多個屬性宣告的成員進行特殊的 null 行為,則 null 狀態是由這些屬性所決定。If an invocation_expression invokes a member that is declared with one or more attributes for special null behavior, the null state is determined by those attributes. 否則,運算式的 null 狀態為其類型的預設 null 狀態。Otherwise the null state of the expression is the default null state of its type.

invocation_expression編譯器不會追蹤的 null 狀態。The null state of an invocation_expression is not tracked by the compiler.


// The result of an invocation_expression is not tracked
if (GetText() is not null)
{
    // Warning: Converting null literal or possible null value to non-nullable type.
    string s = GetText();
    // Warning: Dereference of a possibly null reference.
    Use(s);
}

// Nullable friendly pattern
if (GetText() is string s)
{
    Use(s);
}

string? GetText() => ... 
Use(string s) {  }

項目存取Element access

如果叫 element_access 用以一或多個屬性宣告的索引子來進行特殊的 null 行為,則 null 狀態是由這些屬性所決定。If an element_access invokes an indexer that is declared with one or more attributes for special null behavior, the null state is determined by those attributes. 否則,運算式的 null 狀態為其類型的預設 null 狀態。Otherwise the null state of the expression is the default null state of its type.

object?[] array = ...;
if (array[0] != null)
{
    // Warning: Converting null literal or possible null value to non-nullable type.
    object o = array[0];
    // Warning: Dereference of a possibly null reference.
    Console.WriteLine(o.ToString());
}

// Nullable friendly pattern
if (array[0] is {} o)
{
    Console.WriteLine(o.ToString());
}

基底存取Base access

如果 B 表示封入類型的基底類型, base.I 具有與相同的 null 狀態, ((B)this).I 而且具有與 base[E] 相同的 null 狀態 ((B)this)[E]If B denotes the base type of the enclosing type, base.I has the same null state as ((B)this).I and base[E] has the same null state as ((B)this)[E].

預設運算式Default expressions

default(T) 具有以類型屬性為基礎的 null 狀態 Tdefault(T) has the null state based on the properties of the type T:

  • 如果類型是 不可為 null 類型,則它的狀態為「非 null」If the type is a nonnullable type then it has the null state "not null"
  • 否則,如果類型是型別參數,則它的狀態為「可能是預設值」。Else if the type is a type parameter then it has the null state "maybe default"
  • 否則它的狀態為「可能是 null」Else it has the null state "maybe null"

Null 條件運算式?。Null-conditional expressions ?.

null_conditional_expression具有以運算式類型為基礎的 null 狀態。A null_conditional_expression has the null state based on the expression type. 請注意,這是指的型別 null_conditional_expression ,而不是所叫用之成員的原始型別:Note that this refers to the type of the null_conditional_expression, not the original type of the member being invoked:

  • 如果型別是 可為 null 的實值型別,則其具有 null 狀態「可能為 null」If the type is a nullable value type then it has the null state "maybe null"
  • 否則,如果型別是 可為 null 的型別參數,則它的狀態會是「可能是預設值」。Else if the type is a nullable type parameter then it has the null state "maybe default"
  • 否則它的狀態為「可能是 null」Else it has the null state "maybe null"

轉換運算式Cast expressions

如果轉換運算式叫 (T)E 用使用者定義的轉換,則運算式的 null 狀態為使用者定義轉換之類型的預設 null 狀態。If a cast expression (T)E invokes a user-defined conversion, then the null state of the expression is the default null state for the type of the user-defined conversion. 否則:Otherwise:

  • 如果 T不可為 null 值型別,則會 T 有 null 狀態 "not null"If T is a nonnullable value type then T has the null state "not null"
  • 否則 T ,如果是 可為 null 的實值型別,則會 T 有 null 狀態「可能是 null」Else if T is a nullable value type then T has the null state "maybe null"
  • 如果是類型參數,則為,如果是型別參數,則為,否則為 T U? U T null 狀態「可能是預設值」。Else if T is a nullable type in the form U? where U is a type parameter then T has the null state "maybe default"
  • 否則 T ,如果是 可為 null 的型別,而且 E 具有 null 狀態「可能是 null」或「可能是預設值」,則會 T 有 null 狀態「可能為 null」。Else if T is a nullable type, and E has null state "maybe null" or "maybe default", then T has the null state "maybe null"
  • 否則 T ,如果是型別參數,而且 E 有 null 狀態「可能是 null」或「可能是預設值」,則會 T 有 null 狀態「可能是預設值」。Else if T is a type parameter, and E has null state "maybe null" or "maybe default", then T has the null state "maybe default"
  • Else T 具有與相同的 null 狀態 EElse T has the same null state as E

一元和二元運算子Unary and binary operators

如果一元或二元運算子叫用使用者定義的運算子,則運算式的 null 狀態為使用者定義運算子之類型的預設 null 狀態。If a unary or binary operator invokes an user-defined operator then the null state of the expression is the default null state for the type of the user-defined operator. 否則,它是運算式的 null 狀態。Otherwise it is the null state of the expression.

對字串和委派進行二進位處理的特殊事項 +Something special to do for binary + over strings and delegates?

Await 運算式Await expressions

的 null 狀態 await E 是其類型的預設 null 狀態。The null state of await E is the default null state of its type.

as 運算子The as operator

運算式的 null 狀態會 E as T 優先于型別的屬性(property) TThe null state of an E as T expression depends first on properties of the type T. 如果的型別 T不可為 null ,則 null 狀態為 "not null"。If the type of T is nonnullable then the null state is "not null". 否則,null 狀態取決於從的型別轉換 E 為型別 TOtherwise the null state depends on the conversion from the type of E to type T:

  • 如果轉換是身分識別、裝箱、隱含參考或隱含可為 null 的轉換,則 null 狀態為 null 狀態 EIf the conversion is an identity, boxing, implicit reference, or implicit nullable conversion, then the null state is the null state of E
  • 否則 T ,如果是型別參數,則它的狀態為「可能是預設值」。Else if T is a type parameter then it has the null state "maybe default"
  • 否則它的狀態為「可能是 null」Else it has the null state "maybe null"

Null 聯合運算子The null-coalescing operator

的 null 狀態 E1 ?? E2 是的 null 狀態 E2The null state of E1 ?? E2 is the null state of E2

條件運算子The conditional operator

的 null 狀態 E1 ? E2 : E3 是以和的 null 狀態為基礎 E2 E3The null state of E1 ? E2 : E3 is based on the null state of E2 and E3:

  • 如果兩者都是「非 null」,則 null 狀態為「非 null」。If both are "not null" then the null state is "not null"
  • 否則,如果其中一個是「可能是預設值」,則 null 狀態會是「可能是預設值」。Else if either is "maybe default" then the null state is "maybe default"
  • 否則 null 狀態為 "not null"Else the null state is "not null"

查詢運算式Query expressions

查詢運算式的 null 狀態為其類型的預設 null 狀態。The null state of a query expression is the default null state of its type.

這裡需要其他工作Additional work needed here

指派運算子Assignment operators

E1 = E2 和套用 E1 op= E2 E2 任何隱含轉換之後,都具有相同的 null 狀態。E1 = E2 and E1 op= E2 have the same null state as E2 after any implicit conversions have been applied.

傳播 null 狀態的運算式Expressions that propagate null state

(E)``checked(E)unchecked(E) 全都具有與相同的 null 狀態 E(E), checked(E) and unchecked(E) all have the same null state as E.

不是 null 的運算式Expressions that are never null

下列運算式形式的 null 狀態一律為「非 null」:The null state of the following expression forms is always "not null":

  • this accessthis access
  • 字串插值interpolated strings
  • new 運算式 (物件、委派、匿名物件和陣列建立運算式) new expressions (object, delegate, anonymous object and array creation expressions)
  • typeof 運算式typeof expressions
  • nameof 運算式nameof expressions
  • 匿名函式 (匿名方法和 lambda 運算式) anonymous functions (anonymous methods and lambda expressions)
  • null 容許運算式null-forgiving expressions
  • is 運算式is expressions

嵌套函數Nested functions

Lambda 和區域函式 () 的嵌套函式會被視為方法,但其所捕捉的變數除外。Nested functions (lambdas and local functions) are treated like methods, except in regards to their captured variables. Lambda 或區域函數內部的已捕捉變數初始狀態,是變數的可為 null 狀態與該嵌套函式或 lambda 的所有「使用」的交集。The initial state of a captured variable inside a lambda or local function is the intersection of the nullable state of the variable at all the "uses" of that nested function or lambda. 使用區域函式的方式是呼叫該函式,或將它轉換為委派。A use of a local function is either a call to that function, or where it is converted to a delegate. 使用 lambda 是在來源中定義它的位置。A use of a lambda is the point at which it is defined in source.

型別推斷Type inference

可為 null 的隱含類型區域變數nullable implicitly typed local variables

var 推斷參考型別的批註型別,以及不受限於實值型別的型別參數。var infers an annotated type for reference types, and type parameters that aren't constrained to be a value type. 例如:For instance:

  • 在中, var s = ""; var 會推斷為 string?in var s = ""; the var is inferred as string?.
  • var t = new T(); 具有不受限制的中, T var 會推斷為 T?in var t = new T(); with an unconstrained T the var is inferred as T?.

泛型型別推斷Generic type inference

泛型型別推斷已增強,可協助您決定推斷的參考型別是否應為 null。Generic type inference is enhanced to help decide whether inferred reference types should be nullable or not. 這是最好的工作。This is a best effort. 它可能會產生有關可 null 性條件約束的警告,而且當所選多載的推斷類型套用至引數時,可能會導致可為 null 的警告。It may yield warnings regarding nullability constraints, and may lead to nullable warnings when the inferred types of the selected overload are applied to the arguments.

第一個階段The first phase

可為 null 的參考型別流入初始運算式的界限,如下所述。Nullable reference types flow into the bounds from the initial expressions, as described below. 此外,也引進了兩種新的界限,即 nulldefaultIn addition, two new kinds of bounds, namely null and default are introduced. 其目的是要在 null default 輸入運算式中執行或,這可能會導致推斷的型別可為 null,即使沒有的話也是如此。Their purpose is to carry through occurrences of null or default in the input expressions, which may cause an inferred type to be nullable, even when it otherwise wouldn't. 這甚至適用于可為 null 的實 型別,這些型別已增強,可在推斷程式中挑選 "null f.23"。This works even for nullable value types, which are enhanced to pick up "nullness" in the inference process.

判斷要在第一個階段中新增的界限,其增強方式如下:The determination of what bounds to add in the first phase are enhanced as follows:

如果引數 Ei 有參考型別,則用於推斷的型別會 U 根據的 null 狀態以及其宣告的型別而定 EiIf an argument Ei has a reference type, the type U used for inference depends on the null state of Ei as well as its declared type:

  • 如果宣告的型別是不可為 null 參考型別 U0 或可為 null 的參考型別, U0? 則為If the declared type is a nonnullable reference type U0 or a nullable reference type U0? then
    • 如果的 null 狀態 Ei 為 "not null",則 UU0if the null state of Ei is "not null" then U is U0
    • 如果的 null 狀態 Ei 為 "可能為 null",則 UU0?if the null state of Ei is "maybe null" then U is U0?
  • 否則,如果有宣告的 Ei 型別, U 就是該型別Otherwise if Ei has a declared type, U is that type
  • 否則,如果是,則 Ei null U 為特殊的系結 nullOtherwise if Ei is null then U is the special bound null
  • 否則,如果是,則 Ei default U 為特殊的系結 defaultOtherwise if Ei is default then U is the special bound default
  • 否則不會進行任何推斷。Otherwise no inference is made.

精確、上限和下限推斷Exact, upper-bound and lower-bound inferences

型別推斷 U 類型時 V ,如果 V 是可為 null 的參考型別,則 V0? V0 會使用而非 V 下列子句中的。In inferences from the type U to the type V, if V is a nullable reference type V0?, then V0 is used instead of V in the following clauses.

  • 如果 V 是其中一個未固定的型別變數, U 就會像之前一樣加入精確的上限或下限。If V is one of the unfixed type variables, U is added as an exact, upper or lower bound as before
  • 否則,如果 Unulldefault ,就不會進行任何推斷Otherwise, if U is null or default, no inference is made
  • 否則,如果 U 是可為 null 的參考型別 U0? ,則 U0 會在後面的子句中使用,而不是 UOtherwise, if U is a nullable reference type U0?, then U0 is used instead of U in the subsequent clauses.

本質上是,直接與其中一個未固定的型別變數相關的 null 屬性會保留在其界限內。The essence is that nullability that pertains directly to one of the unfixed type variables is preserved into its bounds. 另一方面,如果推斷會進一步遞迴到來源和目標型別,則會忽略 null 屬性。For the inferences that recurse further into the source and target types, on the other hand, nullability is ignored. 它可能會或可能不相符,但如果沒有,則會在稍後選擇並套用多載時發出警告。It may or may not match, but if it doesn't, a warning will be issued later if the overload is chosen and applied.

修正Fixing

這項規格目前並不是很好的工作,描述當多個界限可互相轉換,但不同時,會發生什麼事。The spec currently does not do a good job of describing what happens when multiple bounds are identity convertible to each other, but are different. 這可能會發生 object 在與之間, dynamic 只有在專案名稱不同的元組類型之間,以及在和之間的類型之間,以及在參考型別之間的不同 C C?This may happen between object and dynamic, between tuple types that differ only in element names, between types constructed thereof and now also between C and C? for reference types.

此外,我們還需要將 "null f.23" 從輸入運算式傳播至結果型別。In addition we need to propagate "nullness" from the input expressions to the result type.

為了處理這些步驟,我們新增了更多階段來修正,現在是:To handle these we add more phases to fixing, which is now:

  1. 將所有範圍中的所有類型收集為候選項, ? 從所有可為 null 的參考型別中移除Gather all the types in all the bounds as candidates, removing ? from all that are nullable reference types
  2. 根據精確、下限和上限 (保持和界限的需求來消除 null 候選人 default) Eliminate candidates based on requirements of exact, lower and upper bounds (keeping null and default bounds)
  3. 刪除沒有隱含轉換給所有其他候選項目的候選項目Eliminate candidates that do not have an implicit conversion to all the other candidates
  4. 如果其餘的候選項目沒有彼此的身分識別轉換,則類型推斷會失敗If the remaining candidates do not all have identity conversions to one another, then type inference fails
  5. 合併 剩餘的候選項目(如下所述)Merge the remaining candidates as described below
  6. 如果產生的候選型別是參考型別或不可為 null 實值型別,而且 所有 的確切 界限或下限 都是可為 null 的實值型別、可為 null 的參考型別,則 null default ? 會加入至產生的候選項,使其成為可為 null 的實值型別或參考型別。If the resulting candidate is a reference type or a nonnullable value type and all of the exact bounds or any of the lower bounds are nullable value types, nullable reference types, null or default, then ? is added to the resulting candidate, making it a nullable value type or reference type.

合併 會在兩個候選型別之間說明。Merging is described between two candidate types. 它是可轉移且可交換的,因此候選項目可以依任何順序以相同的最終結果合併。It is transitive and commutative, so the candidates can be merged in any order with the same ultimate result. 如果兩個候選型別無法互相轉換,則它是未定義的。It is undefined if the two candidate types are not identity convertible to each other.

Merge 函式會採用兩種候選型別,以及 (+ 或) 的方向 -The Merge function takes two candidate types and a direction (+ or -):

  • Merge (TTd) = TMerge(T, T, d) = T
  • Merge (ST?+) = merge (S?T+) = merge (ST+) ?Merge(S, T?, +) = Merge(S?, T, +) = Merge(S, T, +)?
  • Merge (ST?-) = merge (S?T-) = merge (ST-) Merge(S, T?, -) = Merge(S?, T, -) = Merge(S, T, -)
  • Merge (C<S1,...,Sn>C<T1,...,Tn>+) = C< merge (S1T1d1) ,..., merge (SnTndn) >whereMerge(C<S1,...,Sn>, C<T1,...,Tn>, +) = C<Merge(S1, T1, d1),...,Merge(Sn, Tn, dn)>, where
    • di = + 如果的 i ' th 類型參數 C<...> 為協變數di = + if the i'th type parameter of C<...> is covariant
    • di = - 如果的 i ' th 類型參數 C<...> 為非變異或不變di = - if the i'th type parameter of C<...> is contra- or invariant
  • Merge (C<S1,...,Sn>C<T1,...,Tn>-) = C< merge (S1T1d1) ,..., merge (SnTndn) >whereMerge(C<S1,...,Sn>, C<T1,...,Tn>, -) = C<Merge(S1, T1, d1),...,Merge(Sn, Tn, dn)>, where
    • di = - 如果的 i ' th 類型參數 C<...> 為協變數di = - if the i'th type parameter of C<...> is covariant
    • di = + 如果的 i ' th 類型參數 C<...> 為非變異或不變di = + if the i'th type parameter of C<...> is contra- or invariant
  • Merge ((S1 s1,..., Sn sn)(T1 t1,..., Tn tn)d) = ( merge (S1T1d) n1,..., merge (SnTnd) nn)whereMerge((S1 s1,..., Sn sn), (T1 t1,..., Tn tn), d) = (Merge(S1, T1, d)n1,...,Merge(Sn, Tn, d) nn), where
    • ni 如果 siti 不同,或兩者都不存在,則不存在ni is absent if si and ti differ, or if both are absent
    • ni``si如果 siti 相同ni is si if si and ti are the same
  • Merge (objectdynamic) = merge (dynamicobject) = dynamicMerge(object, dynamic) = Merge(dynamic, object) = dynamic

警告Warnings

可能的 null 指派Potential null assignment

可能的 null 取值Potential null dereference

條件約束可 null 性不相符Constraint nullability mismatch

停用注釋內容中的可為 null 類型Nullable types in disabled annotation context

覆寫和執行可 null 性不相符Override and implementation nullability mismatch

特殊 null 行為的屬性Attributes for special null behavior