Tipos de referencia que aceptan valores NULL en C #Nullable reference types in C#
El objetivo de esta característica es:The goal of this feature is to:
- Permite a los desarrolladores expresar si una variable, un parámetro o un resultado de un tipo de referencia está pensado para ser null o no.Allow developers to express whether a variable, parameter or result of a reference type is intended to be null or not.
- Proporcionar advertencias cuando tales variables, parámetros y resultados no se usan según esa intención.Provide warnings when such variables, parameters and results are not used according to that intent.
Expresión de intenciónExpression of intent
El lenguaje ya contiene la T? Sintaxis de los tipos de valor.The language already contains the T? syntax for value types. Es sencillo extender esta sintaxis a tipos de referencia.It is straightforward to extend this syntax to reference types.
Se supone que la intención de un tipo de referencia no adornada T es que no sea NULL.It is assumed that the intent of an unadorned reference type T is for it to be non-null.
Comprobando referencias que aceptan valores NULLChecking of nullable references
Un análisis de flujo realiza un seguimiento de las variables de referencia que aceptan valores NULL.A flow analysis tracks nullable reference variables. Si el análisis considera que no sería null (por ejemplo, después de una comprobación o una asignación), su valor se considerará una referencia no NULL.Where the analysis deems that they would not be null (e.g. after a check or an assignment), their value will be considered a non-null reference.
Una referencia que acepta valores null también se puede tratar explícitamente como no NULL con el operador postfijo x! (el operador "damnit"), para cuando el análisis de flujo no pueda establecer una situación que no sea NULL y sepa que el desarrollador está allí.A nullable reference can also explicitly be treated as non-null with the postfix x! operator (the "damnit" operator), for when flow analysis cannot establish a non-null situation that the developer knows is there.
De lo contrario, se proporciona una advertencia si se desreferencia una referencia que acepta valores NULL o si se convierte en un tipo que no sea NULL.Otherwise, a warning is given if a nullable reference is dereferenced, or is converted to a non-null type.
Se proporciona una advertencia al convertir de S[] a T?[] y de S?[] a T[] .A warning is given when converting from S[] to T?[] and from S?[] to T[].
Se proporciona una advertencia al convertir de C<S> en C<T?> excepto cuando el parámetro de tipo es covariante ( out ) y al convertir de C<S?> en C<T> excepto cuando el parámetro de tipo es contravariante ( in ).A warning is given when converting from C<S> to C<T?> except when the type parameter is covariant (out), and when converting from C<S?> to C<T> except when the type parameter is contravariant (in).
C<T?>Si el parámetro de tipo tiene restricciones que no son NULL, se proporciona una advertencia.A warning is given on C<T?> if the type parameter has non-null constraints.
Comprobando referencias no nulasChecking of non-null references
Se proporciona una advertencia si un literal NULL se asigna a una variable que no es null o se pasa como un parámetro que no es NULL.A warning is given if a null literal is assigned to a non-null variable or passed as a non-null parameter.
También se proporciona una advertencia si un constructor no inicializa explícitamente los campos de referencia no NULL.A warning is also given if a constructor does not explicitly initialize non-null reference fields.
No se puede realizar un seguimiento adecuado de que se inicializan todos los elementos de una matriz de referencias no nulas.We cannot adequately track that all elements of an array of non-null references are initialized. Sin embargo, podríamos emitir una advertencia si no se asigna ningún elemento de una matriz recién creada antes de que la matriz se lea o se pase.However, we could issue a warning if no element of a newly created array is assigned to before the array is read from or passed on. Esto podría controlar el caso común sin ser demasiado ruidoso.That might handle the common case without being too noisy.
Necesitamos decidir si default(T) genera una advertencia o simplemente se trata como si fuese del tipo T? .We need to decide whether default(T) generates a warning, or is simply treated as being of the type T?.
Representación de metadatosMetadata representation
Los elementos gráficos de nulabilidad se deben representar en metadatos como atributos.Nullability adornments should be represented in metadata as attributes. Esto significa que los compiladores de nivel inferior lo ignorarán.This means that downlevel compilers will ignore them.
Necesitamos decidir si solo se incluyen las anotaciones que aceptan valores NULL, o si hay también alguna indicación de si el valor de "ON" no es null en el ensamblado.We need to decide if only nullable annotations are included, or there's also some indication of whether non-null was "on" in the assembly.
GenéricosGenerics
Si un parámetro de tipo T tiene restricciones que no aceptan valores NULL, se trata como un valor que no acepta valores NULL dentro de su ámbito.If a type parameter T has non-nullable constraints, it is treated as non-nullable within its scope.
Si un parámetro de tipo no está restringido o solo tiene restricciones que aceptan valores NULL, la situación es un poco más compleja: Esto significa que el argumento de tipo correspondiente podría admitir valores NULL o que no admita valores NULL.If a type parameter is unconstrained or has only nullable constraints, the situation is a little more complex: this means that the corresponding type argument could be either nullable or non-nullable. Lo más seguro que hacer en esa situación es tratar el parámetro de tipo como valores NULL y que no aceptan valores NULL, lo que permite recibir advertencias cuando se infringe cualquiera de ellos.The safe thing to do in that situation is to treat the type parameter as both nullable and non-nullable, giving warnings when either is violated.
Merece la pena considerar si se deben permitir las restricciones de referencia explícitas que aceptan valores NULL.It is worth considering whether explicit nullable reference constraints should be allowed. Tenga en cuenta, sin embargo, que no se puede evitar que los tipos de referencia que aceptan valores NULL sean restricciones de forma implícita en ciertos casos (restricciones heredadas).Note, however, that we cannot avoid having nullable reference types implicitly be constraints in certain cases (inherited constraints).
La class restricción no es NULL.The class constraint is non-null. Podemos considerar si class? debe ser una restricción válida que acepte valores NULL que indique "tipo de referencia que acepta valores NULL".We can consider whether class? should be a valid nullable constraint denoting "nullable reference type".
Inferencia de tiposType inference
En la inferencia de tipos, si un tipo de contribución es un tipo de referencia que acepta valores NULL, el tipo resultante debe admitir valores NULL.In type inference, if a contributing type is a nullable reference type, the resulting type should be nullable. En otras palabras, se propaga la nulación.In other words, nullness is propagated.
Debemos considerar si el null literal como una expresión participante debe contribuir a la nulación.We should consider whether the null literal as a participating expression should contribute nullness. En la actualidad: para los tipos de valor, conduce a un error, mientras que en los tipos de referencia el valor NULL se convierte correctamente en el tipo simple.It doesn't today: for value types it leads to an error, whereas for reference types the null successfully converts to the plain type.
string? n = "world";
var x = b ? "Hello" : n; // string?
var y = b ? "Hello" : null; // string? or error
var z = b ? 7 : null; // Error today, could be int?
Guía de protección nulaNull guard guidance
Como característica, los tipos de referencia que aceptan valores NULL permiten a los desarrolladores expresar su intención y proporcionar advertencias a través del análisis de flujo si se contradice ese intento.As a feature, nullable reference types allow developers to express their intent, and provide warnings through flow analysis if that intent is contradicted. Hay una pregunta común sobre si se requieren o no las protecciones nulas.There is a common question as to whether or not null guards are necessary.
Ejemplo de protección nulaExample of null guard
public void DoWork(Worker worker)
{
// Guard against worker being null
if (worker is null)
{
throw new ArgumentNullException(nameof(worker));
}
// Otherwise use worker argument
}
En el ejemplo anterior, la DoWork función acepta un Worker y evita que sea posible null .In the previous example, the DoWork function accepts a Worker and guards against it potentially being null. Si el worker argumento es null , la DoWork función será throw .If the worker argument is null, the DoWork function will throw. Con tipos de referencia que aceptan valores NULL, el código del ejemplo anterior hace que el Worker parámetro no sea null .With nullable reference types, the code in the previous example makes the intent that the Worker parameter would not be null. Si la DoWork función era una API pública, como un paquete NuGet o una biblioteca compartida, como guía, debe dejar las protecciones nulas.If the DoWork function was a public API, such as a NuGet package or a shared library - as guidance you should leave null guards in place. Como una API pública, la única garantía de que el autor de la llamada no null está pasando es protegerse frente a ella.As a public API, the only guarantee that a caller isn't passing null is to guard against it.
Intención rápidaExpress intent
Un uso más atractivo del ejemplo anterior es expresar que el Worker parámetro podría ser null , por lo que la protección nula es más adecuada.A more compelling use of the previous example is to express that the Worker parameter could be null, thus making the null guard more appropriate. Si quita la protección nula en el ejemplo siguiente, el compilador advierte que se puede desreferenciar null.If you remove the null guard in the following example, the compiler warns that you may be dereferencing null. Sin embargo, ambas protecciones nulas siguen siendo válidas.Regardless, both null guards are still valid.
public void DoWork(Worker? worker)
{
// Guard against worker being null
if (worker is null)
{
throw new ArgumentNullException(nameof(worker));
}
// Otherwise use worker argument
}
En el caso de las API no públicas, como el código fuente por completo en el control por parte de un desarrollador o un equipo de desarrollo, los tipos de referencia que aceptan valores NULL podrían permitir la eliminación segura de los resguardos nulos en los que los desarrolladores pueden garantizar que no son necesarios.For non-public APIs, such as source code entirely in control by a developer or dev team - the nullable reference types could allow for the safe removal of null guards where the developers can guarantee it is not necessary. La característica puede ayudar con las advertencias, pero no puede garantizar que en la ejecución del código en tiempo de ejecución podría dar como resultado una NullReferenceException .The feature can help with warnings, but it cannot guarantee that at runtime code execution could result in a NullReferenceException.
Últimos cambiosBreaking changes
Las advertencias que no son NULL son un cambio importante evidente en el código existente y deben ir acompañados de un mecanismo de participación.Non-null warnings are an obvious breaking change on existing code, and should be accompanied with an opt-in mechanism.
Menos obvio, las advertencias de tipos que aceptan valores NULL (como se describió anteriormente) son un cambio importante en el código existente en ciertos escenarios en los que la nulabilidad es implícita:Less obviously, warnings from nullable types (as described above) are a breaking change on existing code in certain scenarios where the nullability is implicit:
- Los parámetros de tipo sin restricciones se tratarán como Nullable implícitamente, por lo que asignarlos a
objecto tener acceso a, por ejemplo,ToStringproducirá advertencias.Unconstrained type parameters will be treated as implicitly nullable, so assigning them toobjector accessing e.g.ToStringwill yield warnings. - Si la inferencia de tipos infiere la nulación de las
nullexpresiones, el código existente producirá en ocasiones valores NULL en lugar de tipos que no aceptan valores NULL, lo que puede dar lugar a nuevas advertencias.if type inference infers nullness fromnullexpressions, then existing code will sometimes yield nullable rather than non-nullable types, which can lead to new warnings.
Por lo tanto, las advertencias que aceptan valores null también deben ser opcionalesSo nullable warnings also need to be optional
Por último, la adición de anotaciones a una API existente será un cambio importante para los usuarios que hayan participado en las advertencias al actualizar la biblioteca.Finally, adding annotations to an existing API will be a breaking change to users who have opted in to warnings, when they upgrade the library. Esto también merece la posibilidad de participar o no. "Quiero correcciones de errores, pero no estoy listo para tratar con sus nuevas anotaciones"This, too, merits the ability to opt in or out. "I want the bug fixes, but I am not ready to deal with their new annotations"
En Resumen, debe ser capaz de participar o no de:In summary, you need to be able to opt in/out of:
- Advertencias que aceptan valores NULLNullable warnings
- Advertencias no nulasNon-null warnings
- Advertencias de anotaciones en otros archivosWarnings from annotations in other files
La granularidad de la participación sugiere un modelo similar a un analizador, en el que el usuario puede elegir y deshabilitar swaths de código con las directivas pragma y los niveles de gravedad.The granularity of the opt-in suggests an analyzer-like model, where swaths of code can opt in and out with pragmas and severity levels can be chosen by the user. Además, las opciones por biblioteca ("omitir las anotaciones de JSON.NET hasta que esté listo para tratar con el descenso") pueden expresarse en el código como atributos.Additionally, per-library options ("ignore the annotations from JSON.NET until I'm ready to deal with the fall out") may be expressible in code as attributes.
El diseño de la experiencia de participación o transición es fundamental para el éxito y la utilidad de esta característica.The design of the opt-in/transition experience is crucial to the success and usefulness of this feature. Debemos asegurarnos de que:We need to make sure that:
- Los usuarios pueden adoptar la comprobación de la nulabilidad gradualmente como deseenUsers can adopt nullability checking gradually as they want to
- Los autores de bibliotecas pueden agregar anotaciones de nulabilidad sin miedo a los clientes de la interrupciónLibrary authors can add nullability annotations without fear of breaking customers
- A pesar de esto, no hay una idea de la "pesadilla de configuración".Despite these, there is not a sense of "configuration nightmare"
AjustesTweaks
Podríamos considerar no usar las ? anotaciones en variables locales, pero solo observa si se usan de acuerdo con lo que se les asigna.We could consider not using the ? annotations on locals, but just observing whether they are used in accordance with what gets assigned to them. No me gusta esto; Creo que deberíamos permitir que los usuarios expresen su intención.I don't favor this; I think we should uniformly let people express their intent.
Podríamos considerar una abreviatura T! x en los parámetros, que genera automáticamente una comprobación de NULL en tiempo de ejecución.We could consider a shorthand T! x on parameters, that auto-generates a runtime null check.
Algunos patrones de tipos genéricos, como FirstOrDefault o TryGet , tienen un comportamiento ligeramente extraño con argumentos de tipo que no aceptan valores NULL, ya que generan explícitamente valores predeterminados en determinadas situaciones.Certain patterns on generic types, such as FirstOrDefault or TryGet, have slightly weird behavior with non-nullable type arguments, because they explicitly yield default values in certain situations. Podríamos intentar maticar el sistema de tipos para acomodarlos mejor.We could try to nuance the type system to accommodate these better. Por ejemplo, podríamos permitir ? en parámetros de tipo sin restricciones, aunque el argumento de tipo ya podría aceptar valores NULL.For instance, we could allow ? on unconstrained type parameters, even though the type argument could already be nullable. No dude en que merezca la pena, y conduce a la extrañaidad relacionada con la interacción con tipos de valor que aceptan valores NULL.I doubt that it is worth it, and it leads to weirdness related to interaction with nullable value types.
Tipos de valor que aceptan valores NULLNullable value types
Podríamos considerar adoptar también algunas de las semánticas anteriores para los tipos de valor que aceptan valores NULL.We could consider adopting some of the above semantics for nullable value types as well.
Ya hemos mencionado la inferencia de tipos, donde podríamos deducir int? de (7, null) , en lugar de simplemente producir un error.We already mentioned type inference, where we could infer int? from (7, null), instead of just giving an error.
Otra posibilidad es aplicar el análisis de flujo a tipos de valor que aceptan valores NULL.Another opportunity is to apply the flow analysis to nullable value types. Cuando se consideran no NULL, en realidad podríamos permitir el uso de como tipo que no acepta valores NULL de ciertas maneras (por ejemplo, acceso a miembros).When they are deemed non-null, we could actually allow using as the non-nullable type in certain ways (e.g. member access). Simplemente tenemos que tener cuidado de que se prefieran las cosas que ya puede realizar en un tipo de valor que acepta valores NULL, por motivos de compatibilidad con las copias de seguridad.We just have to be careful that the things that you can already do on a nullable value type will be preferred, for back compat reasons.