Compartir a través de


Tipos de referencia que aceptan valores NULL (referencia de C#)

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.

Puede usar 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. Todas están desactivadas 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 T de referencia con un valor distinto de NULL y nunca puede asignar un valor que pueda ser null.
  • Puede inicializar una variable de un tipo T? de referencia con null o asignar null, pero debe comprobarla con null antes de desreferenciar.
  • Cuando se aplica el operador null-forgiving a una variable m de tipo T?, como en m!, 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.

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. Consulte el artículo sobre cómo establecer una estrategia que acepte 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.

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#.

Consulte también