Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Nota
En este artículo se tratan los tipos de referencia que aceptan valores NULL. También puede declarar tipos de valor que aceptan valores NULL.
Use tipos de referencia que aceptan valores NULL en el código que se encuentra en un contexto que admite valores NULL. Los tipos de referencia que aceptan valores NULL, las advertencias de análisis estático NULL y el operador null-forgiving son características de lenguaje opcionales. Todos están desactivados de forma predeterminada. Puede controlar un contexto que acepta valores NULL en el nivel de proyecto mediante la configuración de compilación o en el código mediante pragmas.
La referencia del lenguaje C# documenta la versión publicada más recientemente del lenguaje C#. También contiene documentación inicial sobre las características de las versiones preliminares públicas de la próxima versión del lenguaje.
La documentación identifica cualquier característica introducida por primera vez en las últimas tres versiones del idioma o en las versiones preliminares públicas actuales.
Sugerencia
Para buscar cuándo se introdujo por primera vez una característica en C#, consulte el artículo sobre el historial de versiones del lenguaje C#.
Importante
Todas las plantillas de proyecto habilitan el contexto que acepta valores NULL para el proyecto. Los proyectos creados con plantillas anteriores no incluyen este elemento y estas características están desactivadas a menos que las habilite en el archivo del proyecto o use pragmas.
En un contexto compatible con valores NULL:
- Debe inicializar una variable de un tipo
Tde referencia con un valor distinto de NULL y nunca puede asignar un valor que pueda sernull. - Puede inicializar una variable de un tipo
T?de referencia connullo asignarnull, pero debe comprobarla connullantes de desreferenciar. - Cuando se aplica el operador null-forgiving a una variable
mde tipoT?, como enm!, la variable se considera que no es NULL.
El compilador aplica las distinciones entre un tipo T de referencia que no acepta valores NULL y un tipo T? de referencia que acepta valores NULL mediante las reglas anteriores. Una variable de tipo T y una variable de tipo T? son el mismo tipo de .NET. En el ejemplo siguiente se declara una cadena que no acepta valores NULL y una cadena que acepta valores NULL y luego se usa el operador null-forgiving para asignar un valor a una cadena que no acepta valores NULL:
string notNull = "Hello";
string? nullable = default;
notNull = nullable!; // null forgiveness
Las variables notNull y nullable ambos usan el String tipo . Dado que los tipos que no aceptan valores NULL y que aceptan valores NULL usan el mismo tipo, no se puede usar un tipo de referencia que acepta valores NULL en varias ubicaciones. En general, no se puede usar un tipo de referencia que acepta valores NULL como una clase base o una interfaz implementada. No se puede usar un tipo de referencia que acepta valores NULL en ninguna expresión de creación de objetos o pruebas de tipos. No se puede usar un tipo de referencia que acepta valores NULL como el tipo de una expresión de acceso de miembro. En los siguientes ejemplos se muestran estas construcciones:
public MyClass : System.Object? // not allowed
{
}
var nullEmpty = System.String?.Empty; // Not allowed
var maybeObject = new object?(); // Not allowed
try
{
if (thing is string? nullableString) // not allowed
Console.WriteLine(nullableString);
} catch (Exception? e) // Not Allowed
{
Console.WriteLine("error");
}
Referencias que aceptan valores NULL y análisis estático
Los ejemplos de la sección anterior muestran la naturaleza de los tipos de referencia que aceptan valores NULL. Los tipos de referencia que aceptan valores NULL no son nuevos tipos de clase, sino anotaciones en tipos de referencia existentes. El compilador utiliza esas anotaciones para ayudarle a encontrar posibles errores de referencia nula en el código. No hay ninguna diferencia en tiempo de ejecución entre un tipo de referencia que no acepta valores NULL y un tipo de referencia que acepta valores NULL. El compilador no agrega ninguna comprobación de tiempo de ejecución para los tipos de referencia que no aceptan valores NULL. Las ventajas radican en el análisis en tiempo de compilación. El compilador genera advertencias que le ayudan a encontrar y corregir posibles errores nulos en el código. Declare su intención y el compilador le advierte cuando el código infringe dicha intención.
Importante
Las anotaciones de referencia que aceptan valores NULL no introducen cambios de comportamiento, pero otras bibliotecas pueden usar la reflexión para generar un comportamiento en tiempo de ejecución diferente para los tipos de referencia que aceptan valores NULL y que no aceptan valores NULL. En concreto, Entity Framework Core lee atributos que aceptan valores NULL. Interpreta una referencia anulable como un valor opcional y una referencia no anulable como un valor obligatorio.
En un contexto habilitado para valores NULL, el compilador realiza un análisis estático de las variables de cualquier tipo de referencia, tanto si admiten valores NULL como si no aceptan valores NULL. El compilador realiza un seguimiento del estado null-state de cada variable de referencia como not-null o maybe-null. El estado predeterminado de una referencia que no acepta valores NULL es not-null. El estado predeterminado de una referencia que acepta valores NULL es maybe-null.
Los tipos de referencia que no aceptan valores NULL siempre deben ser seguros para desreferenciar porque su estado null-state es not-null. Para aplicar esa regla, el compilador emite advertencias si un tipo de referencia que no acepta valores NULL no se inicializa en un valor no NULL. Debe asignar variables locales donde las declare. A cada campo se le debe asignar un valor not-null en un inicializador de campo o en cada constructor. El compilador emite advertencias cuando una referencia que no acepta valores NULL se asigna a una referencia cuyo estado es maybe-null. Por lo general, una referencia que no acepta valores NULL no es NULL y no se emite ninguna advertencia al desreferenciar esas variables.
Nota
Si asigna una expresión maybe-null a un tipo de referencia que no admita valores NULL, el compilador genera una advertencia. A continuación, el compilador genera advertencias para esa variable hasta que se asigna a una expresión not-null.
Puede inicializar o asignar null a tipos de referencia que aceptan valores NULL. Por lo tanto, el análisis estático debe determinar que una variable es not-null antes de desreferenciarla. Si se determina que una referencia que acepta valores NULL es tal vez null, asignarla a una variable de referencia que no acepta valores NULL genera una advertencia del compilador. La consulta siguiente muestra ejemplos de estas advertencias:
public class ProductDescription
{
private string shortDescription;
private string? detailedDescription;
public ProductDescription() // Warning! shortDescription not initialized.
{
}
public ProductDescription(string productDescription) =>
this.shortDescription = productDescription;
public void SetDescriptions(string productDescription, string? details=null)
{
shortDescription = productDescription;
detailedDescription = details;
}
public string GetDescription()
{
if (detailedDescription.Length == 0) // Warning! dereference possible null
{
return shortDescription;
}
else
{
return $"{shortDescription}\n{detailedDescription}";
}
}
public string FullDescription()
{
if (detailedDescription == null)
{
return shortDescription;
}
else if (detailedDescription.Length > 0) // OK, detailedDescription can't be null.
{
return $"{shortDescription}\n{detailedDescription}";
}
return shortDescription;
}
}
En el fragmento de código siguiente se muestra dónde el compilador emite advertencias al utilizar esta clase:
string shortDescription = default; // Warning! non-nullable set to null;
var product = new ProductDescription(shortDescription); // Warning! static analysis knows shortDescription maybe null.
string description = "widget";
var item = new ProductDescription(description);
item.SetDescriptions(description, "These widgets will do everything.");
En los ejemplos anteriores se muestra cómo el análisis estático del compilador determina el estado null-state de las variables de referencia. El compilador aplica las reglas de lenguaje a las asignaciones y comprobaciones de valores NULL para informar de su análisis. El compilador no puede hacer suposiciones sobre la semántica de métodos o propiedades. Si llama a métodos que realizan comprobaciones de valores NULL, el compilador no puede saber que estos métodos afectan al estado null-state de una variable. Puede agregar atributos a las API para informar al compilador sobre la semántica de argumentos y valores devueltos. Muchas API comunes de las bibliotecas de .NET tienen estos atributos. Por ejemplo, el compilador interpreta correctamente IsNullOrEmpty como una comprobación nula. Para obtener más información sobre los atributos que se aplican al análisis estático de estado null-state, consulte el artículo sobre Atributos que aceptan valores NULL.
Contexto que admite un valor NULL
El contexto que acepta valores NULL determina cómo el compilador controla las anotaciones de tipo de referencia que aceptan valores NULL y las advertencias que genera durante el análisis de estado nulo estático. El contexto que acepta valores NULL contiene dos marcas: la configuración de anotación y la configuración de advertencia .
Tanto la configuración de anotación como la de advertencia están deshabilitadas de forma predeterminada para los proyectos existentes. A partir de .NET 6 (C# 10), ambas marcas están habilitadas de forma predeterminada para los nuevos proyectos. El motivo de tener dos banderas distintas para el contexto anulable es facilitar la migración de proyectos grandes que existían antes de la introducción de tipos de referencia anulables.
Para proyectos pequeños, puede habilitar tipos de referencia que aceptan valores NULL, corregir advertencias y continuar. Sin embargo, para proyectos más grandes y soluciones de varios proyectos, ese proceso podría generar un gran número de advertencias. Puede usar pragmas para habilitar los tipos de referencia anulables archivo por archivo a medida que empiece a utilizar tipos de referencia anulables. Las nuevas características que protegen contra el lanzamiento de un System.NullReferenceException pueden ser perjudiciales cuando están activadas en un código base existente.
- Todas las variables de referencia con tipo explícito se interpretan como tipos de referencia que no aceptan valores NULL.
- El significado de la restricción
classen genéricos cambió para significar un tipo de referencia que no acepta valores NULL. - Se generan nuevas advertencias debido a estas nuevas reglas.
El contexto de anotación que acepta valores NULL determina el comportamiento del compilador. Hay cuatro combinaciones para la configuración del contexto que acepta valores NULL:
-
both disabled: el código es nullable-oblivious.
Deshabilitar coincide con el comportamiento antes de que se habilitaran los tipos de referencia que aceptan valores NULL, excepto que la nueva sintaxis genera advertencias en lugar de errores.
- Las advertencias que aceptan valores NULL están deshabilitadas.
- Todas las variables de tipo de referencia son tipos de referencia que aceptan valores NULL.
- El uso del sufijo
?para declarar un tipo de referencia que acepta valores NULL genera una advertencia. - Puede usar el operador que permite un valor NULL,
!, pero no tiene ningún efecto.
-
both enabled: el compilador habilita todo el análisis de referencias nulas y todas las características del lenguaje.
- Todas las nuevas advertencias que aceptan valores NULL están habilitadas.
- Puede usar el sufijo
?para declarar un tipo de referencia que acepta valores NULL. - Las variables de tipo de referencia sin el sufijo
?son tipos de referencia que no aceptan valores NULL. - El operador que permite valores NULL elimina las advertencias de una posible deferencia de
null.
-
advertencias habilitadas: el compilador realiza todos los análisis de valores NULL y emite advertencias cuando el código pueda desreferenciar
null.- Todas las nuevas advertencias que aceptan valores NULL están habilitadas.
- El uso del sufijo
?para declarar un tipo de referencia que acepta valores NULL genera una advertencia. - Todas las variables de tipo de referencia pueden ser NULL. Sin embargo, los miembros tienen el estado null-state de not-null en la llave de apertura de todos los métodos, a menos que se declaren con el sufijo
?. - Puede usar el operador que permite valores NULL,
!.
-
anotaciones habilitadas: el compilador no emite advertencias cuando el código puede hacer referencia
nulla él o cuando se asigna una expresión maybe-null a una variable que no acepta valores NULL.- Todas las nuevas advertencias que aceptan valores NULL están deshabilitadas.
- Puede usar el sufijo
?para declarar un tipo de referencia que acepta valores NULL. - Las variables de tipo de referencia sin el sufijo
?son tipos de referencia que no aceptan valores NULL. - Puede usar el operador que permite un valor NULL,
!, pero no tiene ningún efecto.
Puede establecer el contexto de anotación que acepta valores NULL y el contexto de advertencia que acepta valores NULL para un proyecto mediante el <Nullable> elemento del archivo .csproj . Este elemento configura cómo el compilador interpreta la nulabilidad de los tipos y las advertencias que emite. En la tabla siguiente se muestran los valores permitidos y se resumen los contextos que dichos valores especifican.
| Context | Advertencias de desreferenciación | Advertencias de asignación | Tipos de referencia | Sufijo ? |
Operador ! |
|---|---|---|---|---|---|
disable |
Disabled | Disabled | Todos son anulables. | Genera una advertencia. | No tiene ningún efecto. |
enable |
Habilitado | Habilitado | No es anulable a menos que se declare con ?. |
Declara un tipo que acepta valores NULL. | Suprime las advertencias relativas a una posible asignación null. |
warnings |
Habilitado | No aplicable | Todos aceptan valores NULL, pero los miembros se consideran not-null en la llave de apertura de los métodos | Genera una advertencia. | Suprime las advertencias relativas a una posible asignación null. |
annotations |
Disabled | Disabled | No es anulable a menos que se declare con ?. |
Declara un tipo que acepta valores NULL. | No tiene ningún efecto. |
Las variables de tipo de referencia en el código compilado en un contexto deshabilitado son nullable-oblivious. Puede asignar un valor literal null o una variable maybe-null a una variable que admita un valor NULL "oblivious". Sin embargo, el estado predeterminado de una variable nullable-oblivious es not-null.
Elija la configuración que mejor se adapte a su proyecto:
- Elija disable para los proyectos heredados que no quiere actualizar en función de diagnósticos o nuevas características.
- Elija advertencias para determinar dónde el código puede producir System.NullReferenceException. Puede solucionar esas advertencias antes de modificar el código para habilitar tipos de referencia que no aceptan valores NULL.
- Elija annotations para expresar la intención de diseño antes de habilitar las advertencias.
- Elija enable para nuevos proyectos y proyectos activos en los que quiera protegerse de excepciones de referencia nula.
Ejemplo:
<Nullable>enable</Nullable>
También puede usar directivas para establecer estas mismas marcas en cualquier lugar del código fuente. Estas directivas son más útiles cuando se va a migrar un código base grande.
-
#nullable enable: Establece los indicadores de anotación y advertencia que se van a habilitar. -
#nullable disable: Establece los indicadores de anotación y advertencia que se van a deshabilitar. -
#nullable restore: Restaura la bandera de anotación y la bandera de advertencia en la configuración del proyecto. -
#nullable disable warnings: establece la marca de advertencia que se va a deshabilitar. -
#nullable enable warnings: establece la marca de advertencia que se va a habilitar. -
#nullable restore warnings: Restaura el indicador de advertencia en la configuración del proyecto. -
#nullable disable annotations: establece la marca de anotación que se va a deshabilitar. -
#nullable enable annotations: establece la marca de anotación que se va a habilitar. -
#nullable restore annotations: restaura el indicador de anotación en la configuración del proyecto.
Para cualquier línea de código, puede establecer cualquiera de las siguientes combinaciones:
| Marca de advertencia | Marca de anotación | Uso |
|---|---|---|
| proyecto predeterminado | proyecto predeterminado | Default |
| habilitar | deshabilitar | Corrección de advertencias de análisis |
| habilitar | proyecto predeterminado | Corrección de advertencias de análisis |
| proyecto predeterminado | habilitar | Añadir anotaciones de tipo |
| habilitar | habilitar | Código ya migrado |
| deshabilitar | habilitar | Anotación de código antes de corregir advertencias |
| deshabilitar | deshabilitar | Adición de código heredado al proyecto migrado |
| proyecto predeterminado | deshabilitar | Raramente |
| deshabilitar | proyecto predeterminado | Raramente |
Estas nueve combinaciones le proporcionan un control específico sobre los diagnósticos que emite el compilador para el código. Puede habilitar más características en cualquier área que esté actualizando sin ver advertencias adicionales que aún no está listo para abordar.
Importante
El contexto global que acepta valores NULL no se aplica a los archivos de código generados. En cualquier estrategia, el contexto que admite un valor NULL está deshabilitado para cualquier archivo de código fuente marcado como generado. Esta condición significa que el compilador no anota ninguna API en archivos generados. El compilador no genera advertencias que aceptan valores NULL para los archivos generados. Un archivo se marca como generado de cualquiera de las cuatro maneras siguientes:
- En el archivo .editorconfig, especifique
generated_code = trueen una sección que se aplique a ese archivo. - Coloque
<auto-generated>o<auto-generated/>en un comentario en la parte superior del archivo. Puede estar en cualquier línea de ese comentario, pero el bloque de comentario debe ser el primer elemento del archivo. - Inicie el nombre de archivo con TemporaryGeneratedFile_
- Finalice el nombre de archivo con .designer.cs, .generated.cs, .g.cs o .g.i.cs.
Los generadores pueden participar mediante la #nullable directiva de preprocesador.
De forma predeterminada, las marcas de advertencias y anotaciones que aceptan valores NULL están deshabilitados. Este valor predeterminado significa que el código existente se compila sin cambios y sin generar nuevas advertencias. A partir de .NET 6, los nuevos proyectos incluyen el elemento <Nullable>enable</Nullable> en todas las plantillas de proyecto, estableciendo estas marcas en enabled.
Estas opciones proporcionan dos estrategias distintas para actualizar un código base existente para usar tipos de referencia que aceptan valores NULL.
Establecimiento del contexto que acepta valores NULL
Puede controlar el contexto que acepta valores NULL de dos maneras. En el nivel de proyecto, agregue la configuración del <Nullable>enable</Nullable> proyecto. En un único archivo de código fuente de C#, agregue la #nullable enable pragma para habilitar el contexto que acepta valores NULL. Para obtener más información, consulte Establecimiento de una estrategia que acepta valores NULL. Antes de .NET 6, los nuevos proyectos usan el valor predeterminado, <Nullable>disable</Nullable>. A partir de .NET 6, los proyectos nuevos incluyen el elemento <Nullable>enable</Nullable> en el archivo del proyecto.
Genéricos
Cuando se usa un parámetro de tipo, , Tcomo su homólogo que acepta valores NULL, T?, el argumento de tipo real determina cómo se interpreta .? Tenga en cuenta la siguiente declaración genérica:
public class Box<T>
{
public T Contents { get; set; }
}
Dado que un parámetro de tipo puede ser un tipo de referencia o un tipo de valor, el significado de depende del argumento de tipo que proporciona el autor de T? la llamada. Las reglas siguientes describen lo que T? se resuelve cuando T no tiene restricciones:
- El argumento type es un tipo de referencia que no acepta valores NULL. Para
Box<string>,TesstringyT?esstring?: el tipo de referencia que acepta valores NULL correspondiente. - El argumento type es un tipo de valor. Para
Box<int>,TesintyT?tambiénintes : el mismo tipo de valor. La anotación no tiene ningún efecto en los tipos de valor a menos que el parámetro de tipo tenga lastructrestricción , en cuyo casoT?significa Nullable<T> (int?). - El argumento type ya admite valores NULL. Para
Box<string?>,Tesstring?yT?sigue siendostring?. No se obtiene un tipo "doblemente que acepta valores NULL".
Las restricciones restringen qué argumentos de tipo se permiten. También permiten al compilador el motivo sobre cómo T se puede usar:
-
where T : classrequiere un tipo de referencia que no acepta valores NULL.Box<string>se permite;Box<string?>genera una advertencia. -
where T : class?permite un tipo de referencia que acepta valores NULL o un tipo de referencia que no acepta valores NULL. Tanto comoBox<string>Box<string?>se permiten. -
where T : structrequiere un tipo de valor que no acepta valores NULL.Box<int>se permite;Box<int?>no. Con esta restricción,T?dentro de los medios Nullable<T>genéricos , paraBox<int>,T?esint?. -
where T : notnullrequiere una referencia o un tipo de valor que no acepta valores NULL.Box<string>yBox<int>se permiten;Box<string?>genera una advertencia. -
where T : BaseTyperequiere un tipo de referencia que no acepta valores NULL que deriva deBaseType. Anexe?(where T : BaseType?) para permitir también tipos derivados que aceptan valores NULL.
Las restricciones ayudan al motivo del compilador sobre cómo se usa un parámetro de tipo genérico:
public static T? FirstOrDefault<T>(IEnumerable<T> source)
{
foreach (T item in source)
{
return item;
}
return default;
}
public static void RequireNotNull<T>(T value) where T : notnull
{
ArgumentNullException.ThrowIfNull(value);
}
public static void Generics()
{
string? first = FirstOrDefault<string>([]);
Console.WriteLine(first ?? "<empty>");
RequireNotNull("not null");
}
Especificación del lenguaje C#
Para obtener más información, consulte la sección Tipos de referencia que aceptan valores NULL de la especificación del lenguaje C#.