可以为 null 的引用类型规范Nullable Reference Types Specification
这是一个正在进行的工作-缺少几个部件或这些部件不完整。This is a work in progress - several parts are missing or incomplete.
此功能添加了两种新类型的可为 null 的类型 (可以为 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.
语法Syntax
可以为 null 的引用类型和可以为 null 的类型参数Nullable reference types and nullable type parameters
可以为 null 的引用类型和可以为 null 的类型参数的语法 T?
与可以为 null 的值类型的短格式相同,但没有相应的长格式。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_parameter
In 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_access
as the receiver of amember_access
- 作为
type
中的object_creation_expression
as thetype
in anobject_creation_expression
- 作为
delegate_type
中的delegate_creation_expression
as thedelegate_type
in adelegate_creation_expression
- 作为
type
中的is_expression
,catch_clause
或type_pattern
as thetype
in anis_expression
, acatch_clause
or atype_pattern
- 作为
interface
完全限定的接口成员名称中的as theinterface
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
使用约束的类型形参 notnull
不能是可以为 null 的类型 (可以为 null 的值类型、可以为 null 的引用类型或可以为 null 的类型参数) A 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
约束可用于方法重写或显式实现,以消除 "可以为 null T?
的类型参数" 与 "可以为 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
包容性运算符The null-forgiving operator
后修复后的 !
运算符称为包容性运算符。The post-fix !
operator is called the null-forgiving operator. 它可以应用于 primary_expression 或 null_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_expression
和 null_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
可为空上下文Nullable contexts
源代码的每个行都有 可为 null 的注释上下文 和 可以为 null 的警告上下文。Every line of source code has a nullable annotation context and a nullable warning context. 这些控件控制是否可为 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 # 源代码) 之外的项目级别指定这两个上下文,也可通过预处理器指令在源文件中的任何位置指定 (#nullable
。Both 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.
指令设置上下文 () 它控制后续代码行,直到另一个指令重写它,或直到源文件的结尾。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 的批注和警告上下文设置为 enabled#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 的注释上下文设置为 enabled#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
给定的类型可以具有以下三个 nullabilities 之一: 在意、 不可 null 和 可以为 null。A given type can have one of three nullabilities: oblivious, nonnullable, and nullable.
如果将可能的值分配给 不可 null 类型,则可能会引发警告 null
。Nonnullable types may cause warnings if a potential null
value is assigned to them. 但是,在意 和可以 为 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 检查。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 typeS
is always nonnullable - 可以为 null 的值类型
S?
始终 可以为 nullA nullable value typeS?
is always nullable C
禁用 的注释上下文中的批注引用类型为 在意An unannotated reference typeC
in a disabled annotation context is obliviousC
启用 的批注上下文中的批注引用类型为 不可 nullAn unannotated reference typeC
in an enabled annotation context is nonnullable- 可以为 null 的引用类型可以
C?
为 null (但在 禁用 的批注上下文中可能生成警告) A nullable reference typeC?
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 parameterT
where all constraints (if any) are either nullable types or theclass?
constraint is nullable- 一个类型参数
T
,其中至少有一个约束为 在意 或 不可 null ,或者其中一个struct
或class
或notnull
约束为A type parameterT
where at least one constraint is either oblivious or nonnullable or one of thestruct
orclass
ornotnull
constraints is- 禁用 的注释上下文中的 在意oblivious in a disabled annotation context
- 已启用 的批注上下文中的 不可 nullnonnullable in an enabled annotation context
- 可以为 null 的类型参数
T?
可以为 null,但如果不是值类型,则 已禁用 的批注上下文中会生成一个警告。T
A nullable type parameterT?
is nullable, but a warning is yielded in a disabled annotation context ifT
isn't a value type
在意 vs 不可 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 的值类型不满足约束) 的情况除外 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 状态为 "not null"、"可能为 null" 或 "可能为默认值"。The null state is either "not null", "maybe null", or "maybe default". 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. 区别在于, T
状态为 "可能为 null" 的类型参数意味着该值在合法值的域中, T
但合法值可能包括 null
。The 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
. 如果 "可能为默认值",则表示该值可能在的合法域范围之外 T
。Where 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.
描述类似于明确赋值的空状态转换Describe null state transitions similar to definite assignment
表达式为 NullNull 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
取决于文本的目标类型 default
。The 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 状态 T
:default(T)
has the null state based on the properties of the type T
:
- 如果类型是 不可 null 类型,则它的状态为 "not 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-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"If the type is a nullable value type then it has the null state "maybe null"
- 否则,如果该类型是 可以为 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"IfT
is a nonnullable value type thenT
has the null state "not null" - 否则
T
,如果是 可以为 null 的值类型,则T
具有 null 状态 "可能为 null"Else ifT
is a nullable value type thenT
has the null state "maybe null" - 否则,如果
T
是形式为 null 的类型,U?
其中U
为类型参数,则为T
null 状态 "可能是默认值"Else ifT
is a nullable type in the formU?
whereU
is a type parameter thenT
has the null state "maybe default" - 否则
T
,如果是一个 可以为 null 的类型,并且E
具有 null 状态 "可能是 null" 或 "可能是默认值",则将其T
状态设置为 null。Else ifT
is a nullable type, andE
has null state "maybe null" or "maybe default", thenT
has the null state "maybe null" - 否则
T
,如果是类型参数,并且E
具有 null 状态 "可能是 null" 或 "可能是默认值",则将其T
状态设置为 null 状态 "可能是默认值"Else ifT
is a type parameter, andE
has null state "maybe null" or "maybe default", thenT
has the null state "maybe default" - Else
T
的 null 状态与相同E
ElseT
has the same null state asE
一元运算符和二元运算符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
首先依赖于该类型的属性 T
。The 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
T
:Otherwise the null state depends on the conversion from the type of E
to type T
:
- 如果转换为标识、装箱、隐式引用或隐式可为 null 的转换,则 null 状态为 null 状态
E
If the conversion is an identity, boxing, implicit reference, or implicit nullable conversion, then the null state is the null state ofE
- 否则
T
,如果是类型参数,则它的状态为 "可能是默认值"Else ifT
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
为的空状态 E2
The null state of E1 ?? E2
is the null state of E2
条件运算符The conditional operator
的 null 状态 E1 ? E2 : E3
基于和的 null 状态 E2
E3
:The null state of E1 ? E2 : E3
is based on the null state of E2
and E3
:
- 如果两者都为 "not null",则 null 状态为 "not 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
应用任何隐式转换相同的空状态。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 状态始终为 "not 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
expressionsnameof
表达式nameof
expressions- 匿名函数 (匿名方法和 lambda 表达式) anonymous functions (anonymous methods and lambda expressions)
- 包容性表达式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 或局部函数内捕获的变量的初始状态是该嵌套函数或 lambda 的所有 "使用" 中的变量的可以为 null 的状态的交集。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?
。invar s = "";
thevar
is inferred asstring?
. - 在中,
var t = new T();
T
不受约束的将var
推断为T?
。invar t = new T();
with an unconstrainedT
thevar
is inferred asT?
.
泛型类型推理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 的警告。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. 此外,引入了两种新的界限,即 null
和 default
。In 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"。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 状态及其 Ei
声明的类型:If 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 typeU0
or a nullable reference typeU0?
then- 如果的 null 状态
Ei
为 "not null",则U
为U0
if the null state ofEi
is "not null" thenU
isU0
- 如果的 null 状态
Ei
为 "可能为 null",则U
为U0?
if the null state ofEi
is "maybe null" thenU
isU0?
- 如果的 null 状态
- 否则
Ei
,如果具有已声明的类型,U
则为该类型Otherwise ifEi
has a declared type,U
is that type - 否则,如果为,则
Ei
null
U
为特殊界限null
Otherwise ifEi
isnull
thenU
is the special boundnull
- 否则,如果为,则
Ei
default
U
为特殊界限default
Otherwise ifEi
isdefault
thenU
is the special bounddefault
- 否则,不进行推理。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
则会像以前一样,添加一个精确、上下限或下限IfV
is one of the unfixed type variables,U
is added as an exact, upper or lower bound as before - 否则,如果
U
为null
或default
,则不进行推理Otherwise, ifU
isnull
ordefault
, no inference is made - 否则,如果
U
是可以为 null 的引用类型U0?
,则U0
将在U
后续子句中使用而不是。Otherwise, ifU
is a nullable reference typeU0?
, thenU0
is used instead ofU
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. 另一方面,如果推断将进一步递归到源和目标类型,则会忽略为空性。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" 从输入表达式传播到结果类型。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:
- 将所有边界中的所有类型都作为候选项收集,
?
从所有可为 null 的引用类型中移除Gather all the types in all the bounds as candidates, removing?
from all that are nullable reference types - 根据 (保持和限制) 的精确、下限和上限的要求,消除候选项
null
default
Eliminate candidates based on requirements of exact, lower and upper bounds (keepingnull
anddefault
bounds) - 消除没有隐式转换为所有其他候选项的候选项Eliminate candidates that do not have an implicit conversion to all the other candidates
- 如果剩余的候选项彼此之间没有标识转换,则类型推理将失败If the remaining candidates do not all have identity conversions to one another, then type inference fails
- 按如下所述 合并 剩余候选项Merge the remaining candidates as described below
- 如果生成的候选项是引用类型或不可 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
ordefault
, 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 (
T
,T
, d) = TMerge(T
,T
, d) = T - Merge (
S
,T?
, +) = merge (S?
,T
, +) = merge (S
,T
, +)?
Merge(S
,T?
, +) = Merge(S?
,T
, +) = Merge(S
,T
, +)?
- Merge (
S
,T?
, -) = merge (S?
,T
, -) = merge (S
,T
, -) Merge(S
,T?
, -) = Merge(S?
,T
, -) = Merge(S
,T
, -) - Merge (
C<S1,...,Sn>
,C<T1,...,Tn>
, +) =C<
merge (S1
,T1
, d1),...,
merge (Sn
,Tn
, dn)>
,其中Merge(C<S1,...,Sn>
,C<T1,...,Tn>
, +) =C<
Merge(S1
,T1
, d1),...,
Merge(Sn
,Tn
, dn)>
, wheredi
= + 如果i
的第一个类型参数C<...>
是协变的di
= + if thei
'th type parameter ofC<...>
is covariantdi
= - 如果的i
第一个类型参数C<...>
为逆变或固定di
= - if thei
'th type parameter ofC<...>
is contra- or invariant
- Merge (
C<S1,...,Sn>
,C<T1,...,Tn>
, -) =C<
merge (S1
,T1
, d1),...,
merge (Sn
,Tn
, dn)>
,其中Merge(C<S1,...,Sn>
,C<T1,...,Tn>
, -) =C<
Merge(S1
,T1
, d1),...,
Merge(Sn
,Tn
, dn)>
, wheredi
= - 如果i
的第一个类型参数C<...>
是协变的di
= - if thei
'th type parameter ofC<...>
is covariantdi
= + 如果的i
第一个类型参数C<...>
为逆变或固定di
= + if thei
'th type parameter ofC<...>
is contra- or invariant
- Merge (
(S1 s1,..., Sn sn)
,(T1 t1,..., Tn tn)
, d) =(
merge (S1
,T1
, d)n1,...,
合并 (Sn
,Tn
, d)nn)
,其中Merge((S1 s1,..., Sn sn)
,(T1 t1,..., Tn tn)
, d) =(
Merge(S1
,T1
, d)n1,...,
Merge(Sn
,Tn
, d)nn)
, whereni
如果si
和ti
不同,或者两者都不存在,则不存在ni
is absent ifsi
andti
differ, or if both are absentni
是si
si
和ti
是否相同ni
issi
ifsi
andti
are the same
- Merge (
object
,dynamic
) = merge (dynamic
,object
) =dynamic
Merge(object
,dynamic
) = Merge(dynamic
,object
) =dynamic