Especificación de tipos de referencia que aceptan valores NULLNullable Reference Types Specification

Se trata de un trabajo en curso; faltan varias partes o están incompletas.This is a work in progress - several parts are missing or incomplete.

Esta característica agrega dos nuevos tipos de tipos que aceptan valores NULL (tipos de referencia que aceptan valores NULL y tipos genéricos que aceptan valores NULL) a los tipos de valor que aceptan valores NULL existentes y presenta un análisis de flujo estático con fines de seguridad nula.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.

SintaxisSyntax

Tipos de referencia que aceptan valores NULL y parámetros de tipo que aceptan valores NULLNullable reference types and nullable type parameters

Los tipos de referencia que aceptan valores NULL y los parámetros de tipo que aceptan valores NULL tienen la misma sintaxis T? que la forma abreviada de tipos de valor que aceptan valores NULL, pero no tienen una forma larga correspondiente.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.

Para los fines de la especificación, nullable_type se cambia el nombre de la producción actual a y nullable_value_type nullable_reference_type nullable_type_parameter se agregan las producciones: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_typeEn nullable_reference_type , debe ser un tipo de referencia que no acepte valores NULL (clase, interfaz, delegado o matriz).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_parameterEn nullable_type_parameter debe ser un parámetro de tipo que no esté restringido para ser un tipo de valor.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.

Los tipos de referencia que aceptan valores NULL y los parámetros de tipo que aceptan valores NULL no pueden aparecer en las siguientes posiciones:Nullable reference types and nullable type parameters cannot occur in the following positions:

  • como una clase base o una interfazas a base class or interface
  • como receptor de un member_accessas the receiver of a member_access
  • como type en un object_creation_expressionas the type in an object_creation_expression
  • como delegate_type en un delegate_creation_expressionas the delegate_type in a delegate_creation_expression
  • como type en is_expression , catch_clause o type_patternas the type in an is_expression, a catch_clause or a type_pattern
  • como interface en un nombre completo de miembro de interfazas the interface in a fully qualified interface member name

Se proporciona una advertencia en nullable_reference_type y nullable_type_parameter en un contexto de anotación que acepta valores NULL deshabilitado .A warning is given on a nullable_reference_type and nullable_type_parameter in a disabled nullable annotation context.

class``class?restricción andclass and class? constraint

La class restricción tiene un homólogo que acepta valores NULL class? :The class constraint has a nullable counterpart class?:

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

Se debe crear una instancia de un parámetro de tipo restringido con class (en un contexto de anotación habilitado ) con un tipo de referencia que no acepte valores NULL.A type parameter constrained with class (in an enabled annotation context) must be instantiated with a nonnullable reference type.

Se puede crear una instancia de un parámetro de tipo restringido con class? (o class en un contexto de anotación deshabilitado ) con un tipo de referencia que acepte valores NULL o que no acepte valores 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.

Se proporciona una advertencia en una class? restricción en un contexto de anotación deshabilitado .A warning is given on a class? constraint in a disabled annotation context.

notnull restricciónnotnull constraint

Un parámetro de tipo restringido con notnull no puede ser un tipo que acepta valores NULL (tipo de valor que acepta valores NULL, tipo de referencia que acepta valores NULL o parámetro de tipo que acepta valores 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 restriccióndefault constraint

La default restricción se puede usar en una invalidación de método o en una implementación explícita para eliminar la ambigüedad que T? significa "parámetro de tipo que acepta valores NULL" de "tipo de valor que acepta valores 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>). Si falta la default restricción T? , una sintaxis en una implementación de invalidación o explícita se interpretará como Nullable<T>Lacking the default constraint a T? syntax in an override or explicit implementation will be interpreted as Nullable<T>

Consulta https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/unconstrained-type-parameter-annotations.md#default-constraint.See https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/unconstrained-type-parameter-annotations.md#default-constraint

El operador null-permisivoThe null-forgiving operator

El operador posterior a la corrección ! se denomina operador permisivo nulo.The post-fix ! operator is called the null-forgiving operator. Se puede aplicar en un primary_expression o en un 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
    : '!'
    ;

Por ejemplo:For example:

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

primary_expressionY null_conditional_operations_no_suppression deben ser de un tipo que acepte valores NULL.The primary_expression and null_conditional_operations_no_suppression must be of a nullable type.

El operador postfijo ! no tiene ningún efecto en tiempo de ejecución, sino que se evalúa como el resultado de la expresión subyacente.The postfix ! operator has no runtime effect - it evaluates to the result of the underlying expression. Su único rol es cambiar el estado null de la expresión a "not null" y limitar las advertencias que se dan en su uso.Its only role is to change the null state of the expression to "not null", and to limit warnings given on its use.

Directivas de compilador que aceptan valores NULLNullable compiler directives

#nullable las directivas controlan la anotación y los contextos de advertencia que aceptan valores 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 las directivas se expanden para permitir cambiar el contexto de advertencia que acepta valores NULL:#pragma warning directives are expanded to allow changing the nullable warning context:

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

Por ejemplo:For example:

#pragma warning disable nullable

Contextos que aceptan valores NULLNullable contexts

Cada línea de código fuente tiene un contexto de anotación que acepta valores NULL y un contexto de advertencia que acepta valores NULL.Every line of source code has a nullable annotation context and a nullable warning context. Estos controlan si las anotaciones que aceptan valores NULL tienen efecto y si se proporcionan advertencias de nulabilidad.These control whether nullable annotations have effect, and whether nullability warnings are given. El contexto de anotación de una línea determinada está deshabilitado o habilitado.The annotation context of a given line is either disabled or enabled. El contexto de advertencia de una línea determinada está deshabilitado o habilitado.The warning context of a given line is either disabled or enabled.

Ambos contextos se pueden especificar en el nivel de proyecto (fuera del código fuente de C#) o en cualquier lugar dentro de un archivo de código fuente mediante #nullable directivas de preprocesador.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. Si no se proporciona ninguna configuración de nivel de proyecto, el valor predeterminado es que ambos contextos estén deshabilitados.If no project level settings are provided the default is for both contexts to be disabled.

La #nullable directiva controla la anotación y los contextos de advertencia en el texto de origen y tiene prioridad sobre la configuración de nivel de proyecto.The #nullable directive controls the annotation and warning contexts within the source text, and take precedence over the project-level settings.

Una Directiva establece los contextos que controla para las siguientes líneas de código, hasta que otra directiva la invalida o hasta el final del archivo de código fuente.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.

El efecto de las directivas es el siguiente:The effect of the directives is as follows:

  • #nullable disable: Establece la anotación y los contextos de advertencia que aceptan valores NULL en deshabilitado .#nullable disable: Sets the nullable annotation and warning contexts to disabled
  • #nullable enable: Establece la anotación y los contextos de advertencia que aceptan valores NULL en habilitado .#nullable enable: Sets the nullable annotation and warning contexts to enabled
  • #nullable restore: Restaura los contextos de advertencia y anotación que aceptan valores NULL a la configuración del proyecto.#nullable restore: Restores the nullable annotation and warning contexts to project settings
  • #nullable disable annotations: Establece el contexto de anotación que acepta valores NULL en deshabilitado .#nullable disable annotations: Sets the nullable annotation context to disabled
  • #nullable enable annotations: Establece el contexto de anotación que acepta valores NULL en habilitado#nullable enable annotations: Sets the nullable annotation context to enabled
  • #nullable restore annotations: Restaura el contexto de anotación que acepta valores NULL a la configuración del proyecto.#nullable restore annotations: Restores the nullable annotation context to project settings
  • #nullable disable warnings: Establece el contexto de advertencia que acepta valores NULL en deshabilitado .#nullable disable warnings: Sets the nullable warning context to disabled
  • #nullable enable warnings: Establece el contexto de advertencia que acepta valores NULL en habilitado#nullable enable warnings: Sets the nullable warning context to enabled
  • #nullable restore warnings: Restaura el contexto de advertencia que acepta valores NULL a la configuración del proyecto.#nullable restore warnings: Restores the nullable warning context to project settings

Nulabilidad de tiposNullability of types

Un tipo determinado puede tener uno de tres nullabilities: desconocen, nonnullable y Nullable.A given type can have one of three nullabilities: oblivious, nonnullable, and nullable.

Los tipos que no aceptan valores NULL pueden producir advertencias si null se le asigna un valor potencial.Nonnullable types may cause warnings if a potential null value is assigned to them. Sin embargo, los tipos desconocen y que aceptan valores NULL son "asignable null" y pueden tener null valores asignados sin advertencias.Oblivious and nullable types, however, are "null-assignable" and can have null values assigned to them without warnings.

Los valores de desconocen y los tipos que no aceptan valores NULL se pueden desreferenciar o asignar sin advertencias.Values of oblivious and nonnullable types can be dereferenced or assigned without warnings. Sin embargo, los valores de los tipos que aceptan valores NULL son "produjeron valores NULL" y pueden provocar advertencias al desreferenciarse o asignarse sin una comprobación nula adecuada.Values of nullable types, however, are "null-yielding" and may cause warnings when dereferenced or assigned without proper null checking.

El estado predeterminado NULL de un tipo de rendimiento nulo es "quizás null" o "quizás default".The default null state of a null-yielding type is "maybe null" or "maybe default". El estado Null predeterminado de un tipo de rendimiento que no es NULL es "not null".The default null state of a non-null-yielding type is "not null".

El tipo de tipo y el contexto de anotación que acepta valores NULL que tiene lugar en determina su nulabilidad:The kind of type and the nullable annotation context it occurs in determine its nullability:

  • Un tipo de valor que no acepta valores NULL S siempre es no acepta valores NULLA nonnullable value type S is always nonnullable
  • Un tipo de valor que acepta valores NULL S? siempre admite valores NULLA nullable value type S? is always nullable
  • Un tipo de referencia sin anotar C en un contexto de anotación deshabilitado es desconocenAn unannotated reference type C in a disabled annotation context is oblivious
  • Un tipo de referencia sin anotar C en un contexto de anotación habilitado no admite valores NULLAn unannotated reference type C in an enabled annotation context is nonnullable
  • Un tipo de referencia que acepta valores NULL admite C? valores NULL (pero se puede producir una advertencia en un contexto de anotación deshabilitado )A nullable reference type C? is nullable (but a warning may be yielded in a disabled annotation context)

Los parámetros de tipo también tienen en cuenta las restricciones:Type parameters additionally take their constraints into account:

  • Parámetro de tipo T en el que todas las restricciones (si existen) son tipos que aceptan valores NULL o la class? restricción admite valores NULL .A type parameter T where all constraints (if any) are either nullable types or the class? constraint is nullable
  • Un parámetro de tipo en T el que al menos una restricción es desconocen o que no admite valores NULL , o una de las struct class restricciones o notnull es.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
    • desconocen en un contexto de anotación deshabilitadooblivious in a disabled annotation context
    • no acepta valores NULL en un contexto de anotación habilitadononnullable in an enabled annotation context
  • Un parámetro de tipo que acepta valores NULL admite T? valores NULL, pero se produce una advertencia en un contexto de anotación deshabilitado si T no es un tipo de valorA nullable type parameter T? is nullable, but a warning is yielded in a disabled annotation context if T isn't a value type

Desconocen frente a no NullableOblivious vs nonnullable

typeSe considera que se produce en un contexto de anotación determinado cuando el último token del tipo está dentro de ese contexto.A type is deemed to occur in a given annotation context when the last token of the type is within that context.

Si un tipo de referencia determinado C en el código fuente se interpreta como desconocen o no acepta valores NULL, depende del contexto de la anotación de ese código fuente.Whether a given reference type C in source code is interpreted as oblivious or nonnullable depends on the annotation context of that source code. Pero una vez establecido, se considera parte de ese tipo y "viaja con él", por ejemplo, durante la sustitución de los argumentos de tipo genérico.But once established, it is considered part of that type, and "travels with it" e.g. during substitution of generic type arguments. Es como si hubiera una anotación como ? en el tipo, pero no es visible.It is as if there is an annotation like ? on the type, but invisible.

RestriccionesConstraints

Los tipos de referencia que aceptan valores NULL se pueden usar como restricciones genéricas.Nullable reference types can be used as generic constraints.

class? es una nueva restricción que denota "posible tipo de referencia que acepta valores NULL", mientras que class en un contexto de anotación habilitado denota "tipo de referencia que no acepta valores NULL".class? is a new constraint denoting "possibly nullable reference type", whereas class in an enabled annotation context denotes "nonnullable reference type".

default es una nueva restricción que denota un parámetro de tipo que no se sabe que es un tipo de referencia o de valor.default is a new constraint denoting a type parameter that isn't known to be a reference or value type. Solo se puede usar en métodos invalidados y implementados explícitamente.It can only be used on overridden and explicitly implemented methods. Con esta restricción, T? significa un parámetro de tipo que acepta valores NULL, en lugar de ser un método abreviado para Nullable<T> .With this constraint, T? means a nullable type parameter, as opposed to being a shorthand for Nullable<T>.

notnull es una nueva restricción que denota un parámetro de tipo que no acepta valores NULL.notnull is a new constraint denoting a type parameter that is nonnullable.

La nulabilidad de un argumento de tipo o de una restricción no afecta a si el tipo satisface la restricción, excepto en el caso de que ya sea el caso hoy (los tipos de valor que aceptan valores NULL no satisfacen la struct restricción).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). Sin embargo, si el argumento de tipo no satisface los requisitos de nulabilidad de la restricción, se puede proporcionar una advertencia.However, if the type argument does not satisfy the nullability requirements of the constraint, a warning may be given.

Estado NULL y seguimiento nuloNull state and null tracking

Cada expresión de una ubicación de origen determinada tiene un Estado null, que indica si se considera que puede evaluarse como null.Every expression in a given source location has a null state, which indicated whether it is believed to potentially evaluate to null. El estado nulo es "no NULL", "quizás null" o "quizás default".The null state is either "not null", "maybe null", or "maybe default". El estado NULL se usa para determinar si se debe proporcionar una advertencia sobre las conversiones y desreferenciaciones no seguras de NULL.The null state is used to determine whether a warning should be given about null-unsafe conversions and dereferences.

La distinción entre "quizás null" y "quizás default" es sutil y se aplica a los parámetros de tipo.The distinction between "maybe null" and "maybe default" is subtle and applies to type parameters. La distinción es que un parámetro de tipo T que tiene el estado "quizás null" significa que el valor está en el dominio de valores válidos para, T sin embargo, ese valor legal puede incluir 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. En los casos en los que el valor predeterminado es "quizás default", el valor puede estar fuera del dominio legal de los valores de T .Where as a "maybe default" means that the value may be outside the legal domain of values for T.

Ejemplo: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);
}

Seguimiento de valores NULL para variablesNull tracking for variables

En el caso de ciertas expresiones que indican variables, campos o propiedades, se realiza un seguimiento del estado Null entre las repeticiones, en función de las asignaciones a ellos, las pruebas realizadas en ellos y el flujo de control entre ellos.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. Esto es similar a la forma en que se realiza el seguimiento de la asignación definitiva para las variables.This is similar to how definite assignment is tracked for variables. Las expresiones de las que se ha realizado un seguimiento son las siguientes:The tracked expressions are the ones of the following form:

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

Donde los identificadores denotan campos o propiedades.Where the identifiers denote fields or properties.

El estado null de las variables a las que se hace seguimiento es "not null" en código inaccesible.The null state for tracked variables is "not null" in unreachable code. Esto sigue otras decisiones relacionadas con el código inaccesible como la consideración de la asignación definitiva de todas las variables locales.This follows other decisions around unreachable code like considering all locals to be definitely assigned.

Describir las transiciones de estado Null similares a las asignaciones definitivasDescribe null state transitions similar to definite assignment

Estado null para expresionesNull state for expressions

El estado null de una expresión se deriva de su forma y tipo, y del estado null de las variables implicadas en él.The null state of an expression is derived from its form and type, and from the null state of variables involved in it.

LiteralesLiterals

El estado null de un null literal depende del tipo de destino de la expresión.The null state of a null literal depends on the target type of the expression. Si el tipo de destino es un parámetro de tipo restringido a un tipo de referencia, es "quizás default".If the target type is a type parameter constrained to a reference type then it's "maybe default". En caso contrario, es "quizás null".Otherwise it is "maybe null".

El estado null de un default literal depende del tipo de destino del default literal.The null state of a default literal depends on the target type of the default literal. Un default literal con el tipo de destino T tiene el mismo estado null que la default(T) expresión.A default literal with target type T has the same null state as the default(T) expression.

El estado null de cualquier otro literal es "not null".The null state of any other literal is "not null".

Nombres simplesSimple names

Si un simple_name no se clasifica como un valor, su estado NULL es "not null".If a simple_name is not classified as a value, its null state is "not null". En caso contrario, se trata de una expresión de la que se realiza un seguimiento y su estado NULL es el estado NULL del que se realiza el seguimiento en esta ubicación de origen.Otherwise it is a tracked expression, and its null state is its tracked null state at this source location.

Acceso a miembrosMember access

Si un member_access no se clasifica como un valor, su estado NULL es "not null".If a member_access is not classified as a value, its null state is "not null". De lo contrario, si se trata de una expresión de la que se realiza un seguimiento, su estado NULL es el estado NULL del que se realiza el seguimiento en esta ubicación de origen.Otherwise, if it is a tracked expression, its null state is its tracked null state at this source location. De lo contrario, su estado NULL es el estado Null predeterminado de su tipo.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;
}

Expresiones de invocaciónInvocation expressions

Si invocation_expression invoca un miembro declarado con uno o varios atributos para un comportamiento especial nulo, el estado NULL se determina mediante esos atributos.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. De lo contrario, el estado null de la expresión es el estado Null predeterminado de su tipo.Otherwise the null state of the expression is the default null state of its type.

invocation_expressionEl compilador no realiza el seguimiento del estado null de un.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) {  }

Acceso a elementosElement access

Si element_access invoca un indexador que se declara con uno o más atributos para un comportamiento especial nulo, los atributos determinan el estado 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. De lo contrario, el estado null de la expresión es el estado Null predeterminado de su tipo.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());
}

Acceso baseBase access

Si B denota el tipo base del tipo envolvente, base.I tiene el mismo estado null que ((B)this).I y base[E] tiene el mismo estado null que ((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].

Expresiones predeterminadasDefault expressions

default(T) tiene el estado null en función de las propiedades del tipo T :default(T) has the null state based on the properties of the type T:

  • Si el tipo es un tipo que no acepta valores NULL , tiene el estado null "not null"If the type is a nonnullable type then it has the null state "not null"
  • De lo contrario, si el tipo es un parámetro de tipo, tiene el estado null "quizás default".Else if the type is a type parameter then it has the null state "maybe default"
  • En caso contrario, tiene el estado null "quizás null"Else it has the null state "maybe null"

Expresiones condicionales nulas?.Null-conditional expressions ?.

Un null_conditional_expression tiene el estado Null basado en el tipo de expresión.A null_conditional_expression has the null state based on the expression type. Tenga en cuenta que esto hace referencia al tipo de null_conditional_expression , no al tipo original del miembro que se invoca:Note that this refers to the type of the null_conditional_expression, not the original type of the member being invoked:

  • Si el tipo es un tipo de valor que acepta valores NULL , tiene el estado null "maybe null"If the type is a nullable value type then it has the null state "maybe null"
  • De lo contrario, si el tipo es un parámetro de tipo que acepta valores NULL , tiene el estado null "quizás default".Else if the type is a nullable type parameter then it has the null state "maybe default"
  • En caso contrario, tiene el estado null "quizás null"Else it has the null state "maybe null"

Expresiones de conversiónCast expressions

Si una expresión de conversión (T)E invoca una conversión definida por el usuario, el estado null de la expresión es el estado predeterminado NULL para el tipo de la conversión definida por el usuario.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. De lo contrario:Otherwise:

  • Si T es un tipo de valor que no acepta valores NULL , T tiene el estado null "not null"If T is a nonnullable value type then T has the null state "not null"
  • TDe lo contrario, si es un tipo de valor que acepta valores NULL , T tiene el estado null "maybe null"Else if T is a nullable value type then T has the null state "maybe null"
  • De lo contrario T , si es un tipo que acepta valores NULL en el formulario U? U , donde es un parámetro de tipo, T tiene el estado null "quizás default".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"
  • De lo contrario, si T es un tipo que acepta valores NULL y E tiene el estado null "maybe null" o "quizás default", T tiene el estado null "maybe 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"
  • De lo contrario, si T es un parámetro de tipo y E tiene el estado null "maybe null" o "quizás default", T tiene el estado null "quizás default".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 tiene el mismo estado null que EElse T has the same null state as E

Operadores unarios y binariosUnary and binary operators

Si un operador unario o binario invoca a un operador definido por el usuario, el estado null de la expresión es el estado predeterminado NULL para el tipo del operador definido por el usuario.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. De lo contrario, es el estado null de la expresión.Otherwise it is the null state of the expression.

¿Algo especial de hacer para binario + en cadenas y delegados?Something special to do for binary + over strings and delegates?

Expresiones AwaitAwait expressions

El estado null de await E es el estado Null predeterminado de su tipo.The null state of await E is the default null state of its type.

El operador asThe as operator

El estado null de una E as T expresión depende primero de las propiedades del tipo T .The null state of an E as T expression depends first on properties of the type T. Si el tipo de T no admite valores NULL , el estado NULL es "not null".If the type of T is nonnullable then the null state is "not null". En caso contrario, el estado Null depende de la conversión del tipo de E al tipo T :Otherwise the null state depends on the conversion from the type of E to type T:

  • Si la conversión es una identidad, una conversión boxing, una referencia implícita o una conversión implícita que acepta valores NULL, el estado NULL es el estado null de EIf the conversion is an identity, boxing, implicit reference, or implicit nullable conversion, then the null state is the null state of E
  • TDe lo contrario, si es un parámetro de tipo, tiene el estado null "quizás default".Else if T is a type parameter then it has the null state "maybe default"
  • En caso contrario, tiene el estado null "quizás null"Else it has the null state "maybe null"

Operador de uso combinado de nullThe null-coalescing operator

El estado null de E1 ?? E2 es el estado nulo de E2The null state of E1 ?? E2 is the null state of E2

El operador condicionalThe conditional operator

El estado null de E1 ? E2 : E3 se basa en el estado null de E2 y E3 :The null state of E1 ? E2 : E3 is based on the null state of E2 and E3:

  • Si ambos son "not null", el estado NULL es "not null"If both are "not null" then the null state is "not null"
  • De lo contrario, si es "quizás default", el estado NULL es "quizás default".Else if either is "maybe default" then the null state is "maybe default"
  • De lo contrario, el estado NULL es "not null"Else the null state is "not null"

Expresiones de consultaQuery expressions

El estado null de una expresión de consulta es el estado Null predeterminado de su tipo.The null state of a query expression is the default null state of its type.

Trabajo adicional necesario aquíAdditional work needed here

Operadores de asignaciónAssignment operators

E1 = E2 y E1 op= E2 tienen el mismo estado null que E2 cuando se ha aplicado cualquier conversión implícita.E1 = E2 and E1 op= E2 have the same null state as E2 after any implicit conversions have been applied.

Expresiones que propagan el estado nullExpressions that propagate null state

(E)``checked(E)y unchecked(E) todos tienen el mismo estado null que E .(E), checked(E) and unchecked(E) all have the same null state as E.

Expresiones que nunca son NULLExpressions that are never null

El estado null de los siguientes formatos de expresión siempre es "not null":The null state of the following expression forms is always "not null":

  • this accessthis access
  • cadenas interpoladasinterpolated strings
  • new expresiones (expresiones de objeto, delegado, objeto anónimo y creación de matriz)new expressions (object, delegate, anonymous object and array creation expressions)
  • Expresiones typeoftypeof expressions
  • Expresiones nameofnameof expressions
  • funciones anónimas (métodos anónimos y expresiones lambda)anonymous functions (anonymous methods and lambda expressions)
  • Expresiones permisivo nulasnull-forgiving expressions
  • Expresiones isis expressions

Funciones anidadasNested functions

Las funciones anidadas (expresiones lambda y funciones locales) se tratan como métodos, excepto en lo que respecta a las variables capturadas.Nested functions (lambdas and local functions) are treated like methods, except in regards to their captured variables. El estado inicial de una variable capturada dentro de una función lambda o local es la intersección del estado que acepta valores NULL de la variable en todos los "usos" de esa función anidada o expresión 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. El uso de una función local es una llamada a esa función, o bien, donde se convierte en un delegado.A use of a local function is either a call to that function, or where it is converted to a delegate. El uso de una expresión lambda es el punto en el que se define en el origen.A use of a lambda is the point at which it is defined in source.

Inferencia de tiposType inference

variables locales con tipo implícito que aceptan valores NULLnullable implicitly typed local variables

var deduce un tipo anotado para los tipos de referencia y los parámetros de tipo que no están restringidos para ser un tipo de valor.var infers an annotated type for reference types, and type parameters that aren't constrained to be a value type. Por ejemplo:For instance:

  • en var s = ""; var , se deduce como string? .in var s = ""; the var is inferred as string?.
  • en var t = new T(); con una sin restricciones T var , se deduce como T? .in var t = new T(); with an unconstrained T the var is inferred as T?.

Inferencia de tipo genéricoGeneric type inference

La inferencia de tipos genéricos se ha mejorado para ayudar a decidir si los tipos de referencia deducidos deben admitir valores NULL o no.Generic type inference is enhanced to help decide whether inferred reference types should be nullable or not. Se trata de un mejor esfuerzo.This is a best effort. Puede producir advertencias con respecto a las restricciones de nulabilidad y puede dar lugar a advertencias que aceptan valores NULL cuando los tipos deducidos de la sobrecarga seleccionada se aplican a los argumentos.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.

La primera faseThe first phase

Los tipos de referencia que aceptan valores NULL fluyen en los límites de las expresiones iniciales, como se describe a continuación.Nullable reference types flow into the bounds from the initial expressions, as described below. Además, null se introducen dos nuevos tipos de límites, es decir, y default .In addition, two new kinds of bounds, namely null and default are introduced. Su finalidad es llevar a cabo a través de null las apariciones de o default en las expresiones de entrada, lo que puede hacer que un tipo deducido acepte valores NULL, aunque no lo sea.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. Esto funciona incluso para los tipos de valor que aceptan valores NULL, que se han mejorado para recoger la "nulación" en el proceso de inferencia.This works even for nullable value types, which are enhanced to pick up "nullness" in the inference process.

La determinación de los límites que se van a agregar en la primera fase se ha mejorado como se indica a continuación:The determination of what bounds to add in the first phase are enhanced as follows:

Si un argumento Ei tiene un tipo de referencia, el tipo que se U usa para la inferencia depende del estado null de, así Ei como de su tipo declarado: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:

  • Si el tipo declarado es un tipo de referencia que no admite valores NULL U0 o un tipo de referencia que acepta valores NULL U0? , entoncesIf the declared type is a nonnullable reference type U0 or a nullable reference type U0? then
    • Si el estado null de Ei es "not null", entonces U``U0if the null state of Ei is "not null" then U is U0
    • Si el estado null de Ei es "quizás null", entonces U``U0?if the null state of Ei is "maybe null" then U is U0?
  • De lo contrario Ei , si tiene un tipo declarado, U es de ese tipo.Otherwise if Ei has a declared type, U is that type
  • De lo Ei contrario null , si es, U el enlace especial nullOtherwise if Ei is null then U is the special bound null
  • De lo Ei contrario default , si es, U el enlace especial defaultOtherwise if Ei is default then U is the special bound default
  • En caso contrario, no se realiza ninguna inferencia.Otherwise no inference is made.

Inferencias exactas, de límite superior y de límite inferiorExact, upper-bound and lower-bound inferences

En las inferencias del tipo U al tipo V , si V es un tipo de referencia que acepta valores NULL V0? , V0 se usa en lugar de V en las cláusulas siguientes.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.

  • Si V es una de las variables de tipo sin corregir, U se agrega como un límite inferior, superior o inferior como antes.If V is one of the unfixed type variables, U is added as an exact, upper or lower bound as before
  • De lo contrario, si U es null o default , no se realiza ninguna inferencia.Otherwise, if U is null or default, no inference is made
  • De lo contrario, si U es un tipo de referencia que acepta valores NULL U0? , U0 se utiliza en lugar de U en las cláusulas posteriores.Otherwise, if U is a nullable reference type U0?, then U0 is used instead of U in the subsequent clauses.

La esencia es que la nulabilidad que pertenece directamente a una de las variables de tipo sin Fixed se conserva en sus límites.The essence is that nullability that pertains directly to one of the unfixed type variables is preserved into its bounds. Por otra parte, para las inferencias que se recorren más en los tipos de origen y de destino, se omite la nulabilidad.For the inferences that recurse further into the source and target types, on the other hand, nullability is ignored. Puede o no coincidir, pero si no es así, se emitirá una advertencia más adelante si se elige la sobrecarga y se aplica.It may or may not match, but if it doesn't, a warning will be issued later if the overload is chosen and applied.

Corrección deFixing

Actualmente, la especificación no realiza un buen trabajo de describir lo que sucede cuando varios límites son convertibles entre sí, pero son diferentes.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. Esto puede ocurrir entre object y dynamic , entre tipos de tupla que solo difieren en los nombres de elemento, entre los tipos construidos en él y ahora también entre C y C? para los tipos de referencia.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.

Además, debemos propagar la "nulación" de las expresiones de entrada al tipo de resultado.In addition we need to propagate "nullness" from the input expressions to the result type.

Para controlar estos agregamos más fases para corregir, que ahora es:To handle these we add more phases to fixing, which is now:

  1. Recopile todos los tipos de todos los límites como candidatos, quitando ? de todos los tipos de referencia que aceptan valores NULLGather all the types in all the bounds as candidates, removing ? from all that are nullable reference types
  2. Eliminación de candidatos en función de los requisitos de los límites exactos, inferiores y superiores (mantenimiento null y default límites)Eliminate candidates based on requirements of exact, lower and upper bounds (keeping null and default bounds)
  3. Eliminación de candidatos que no tienen una conversión implícita a todos los demás candidatosEliminate candidates that do not have an implicit conversion to all the other candidates
  4. Si los candidatos restantes no tienen conversiones de identidad entre sí, se produce un error en la inferencia de tiposIf the remaining candidates do not all have identity conversions to one another, then type inference fails
  5. Combine los candidatos restantes como se describe a continuaciónMerge the remaining candidates as described below
  6. Si el candidato resultante es un tipo de referencia o un tipo de valor que no acepta valores NULL y todos los límites exactos o alguno de los límites inferiores son tipos de valor que aceptan valores NULL, tipos de referencia que aceptan valores NULL o null default , ? se agregan al candidato resultante, lo que lo convierte en un tipo de valor que acepta valores NULL o un tipo de referencia.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.

La combinación se describe entre dos tipos candidatos.Merging is described between two candidate types. Es transitiva y conmutable, por lo que los candidatos se pueden combinar en cualquier orden con el mismo resultado final.It is transitive and commutative, so the candidates can be merged in any order with the same ultimate result. No se define si los dos tipos candidatos no son una identidad convertible entre sí.It is undefined if the two candidate types are not identity convertible to each other.

La función Merge toma dos tipos de candidatos y una dirección ( + o - ):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) > , dondeMerge(C<S1,...,Sn>, C<T1,...,Tn>, +) = C<Merge(S1, T1, d1),...,Merge(Sn, Tn, dn)>, where
    • di = + Si el i parámetro de tipo TH de C<...> es covariantedi = + if the i'th type parameter of C<...> is covariant
    • di = - Si el i parámetro de tipo ' th of C<...> es compensa-or invariabledi = - if the i'th type parameter of C<...> is contra- or invariant
  • Merge( C<S1,...,Sn> , C<T1,...,Tn> , - ) = C< Merge( S1 , T1 , D1) ,..., Merge( Sn , Tn , DN) > , dondeMerge(C<S1,...,Sn>, C<T1,...,Tn>, -) = C<Merge(S1, T1, d1),...,Merge(Sn, Tn, dn)>, where
    • di = - Si el i parámetro de tipo TH de C<...> es covariantedi = - if the i'th type parameter of C<...> is covariant
    • di = + Si el i parámetro de tipo ' th of C<...> es compensa-or invariabledi = + if the i'th type parameter of C<...> is contra- or invariant
  • Merge( (S1 s1,..., Sn sn) , (T1 t1,..., Tn tn) , d) = ( Merge( S1 , T1 , d) n1,..., Merge( Sn , Tn , d) nn) , dondeMerge((S1 s1,..., Sn sn), (T1 t1,..., Tn tn), d) = (Merge(S1, T1, d)n1,...,Merge(Sn, Tn, d) nn), where
    • ni está ausente si si y ti difieren, o si ambos están ausentesni is absent if si and ti differ, or if both are absent
    • ni es si si si y ti son iguales.ni is si if si and ti are the same
  • Merge( object , dynamic ) = Merge( dynamic , object ) = dynamicMerge(object, dynamic) = Merge(dynamic, object) = dynamic

AdvertenciasWarnings

Posible asignación nullPotential null assignment

Desreferenciación potencial nullPotential null dereference

No coincide la nulabilidad de restriccionesConstraint nullability mismatch

Tipos que aceptan valores NULL en el contexto de anotación deshabilitadoNullable types in disabled annotation context

Incoherencia en la nulabilidad de invalidación e implementaciónOverride and implementation nullability mismatch

Atributos para un comportamiento especial nuloAttributes for special null behavior