Referencias anulables en C# 8.0 y posteriores (III): aplicaciones del operador !

[NOTA: Este artículo asume que todo el código que se presenta estará contenido en proyectos que tienen activadas las referencias anulables. Cómo activar las referencias anulables para un proyecto de C# y Visual Studio 2019 se describe aquí].

En la entrada anterior hablamos del llamado null-forgiving operator («operador para perdonar los nulos»), introducido en C# 8.0 como parte del soporte para las referencias anulables (nullable references). Se trata de un operador unario posfijo que se representa mediante un signo de admiración (!), y que informa al compilador de que una expresión de un tipo por referencia no es nula, para que éste no genere las advertencias asociadas a la posible anulabilidad de la expresión.

Según menciona aquí el gran Jon Skeet, autor de «C# in Depth», existen dos escenarios principales en los que el uso del operador ! (que él llama «damn-it», que significa más o menos «¡Maldita sea!») cobra sentido (cito cuasi-textualmente):

  1. Cuando existen invariantes en nuestro código que nos permiten saber más que el compilador sobre la anulabilidad de una expresión. En eso de que «sabemos más que el compilador»  este operador se asemeja a una conversión explícita (cast). Recuerde, no obstante, que no es lo mismo que un cast en términos de comportamiento: no se hace comprobación alguna en tiempo de ejecución.
  2. Cuando se crean test unitarios de validación de argumentos nulos.

Pero en dicho artículo Skeet presenta precisamente una situación que no responde a ninguno de los dos patrones anteriores. Y es que, como ocurre con las meigas, «haberlas, haylas» :-). Como casi siempre, lo importante es conocer bien el lenguaje y aplicar juiciosamente sus características cuando se reconozca la ocasión.

El ejemplo de nuestra entrada anterior es un caso típico del primero de los dos escenarios. Aquí voy a presentar un código algo diferente, aunque igualmente sencillo:

    01  public class Program
    02  {
    03      static void Main()
    04      {
    05          var input = GetName(); // string? GetName() is defined elsewhere 
    06          System.Console.WriteLine(FormatName(input));
    07      }
    08
    09      public static string FormatName(string rawName)
    10      {
    11          if (string.IsNullOrWhiteSpace(rawName))
    12              return string.Empty;
    13
    14          return rawName.Trim().ToUpperInvariant();        
    15      }
    16  }

Suponga que sabemos que GetName, aún cuando su tipo de retorno es string?, nos devolverá siempre una cadena de caracteres no nula. Por su parte, el método FormatName aplica un formato específico a la cadena de caracteres que recibe. Observe que el argumento rawName es de tipo string y no string?, y que funcionará correctamente incluso si se le pasa el valor null como parámetro (aunque esto último ya el compilador tampoco lo sabe). Dado que el tipo de input es formalmente string?, al compilar recibiremos la advertencia:

    CS8604: Possible null reference argument for parameter 'rawName' [line 06]

Como en el ejemplo de la entrada anterior, aquí podremos indicarle al compilador que sabemos más que él y que puede ahorrarse la advertencia aplicando el operador ! al argumento input en la línea 06.

Suponga ahora que queremos escribir una prueba unitaria (unit test) para el método FormatName y comprobar que cuando al método se le pasa null como entrada, la salida es una cadena vacía. La solucion obvia sería la siguiente:

    01  [TestClass] 
    02  public class ProgramTests
    03  {
    04      [TestMethod]
    05      public void FormatNameTest1()
    06      {
    07          // utilice null! para suprimir la advertencia
    08          var result = Program.FormatName(null); 
    09          Assert.IsTrue(result == string.Empty));
    10      }
    11  }

Esta prueba se compila y ejecuta correctamente, pero el compilador nos dará la advertencia:

    CS8625: Cannot convert null literal to non-nullable reference type [line 08]

La advertencia tiene toda la lógica del mundo: si el parámetro es no anulable, ¿para qué vamos a pasarle null a posta? Pero precisamente lo que se quiere aquí es validar el comportamiento del código en esa situación «anormal». ¿Cómo librarnos en este caso de la advertencia? Pues de manera similar a como lo hicimos en el ejemplo anterior: anexando el operador ! a la expresión que se utiliza como argumento de la llamada, en este caso null. Este es un ejemplo típico del segundo tipo de escenarios para la utilización del operador ! a los que se refería Skeet en el artículo antes citado.

En nuestra próxima entrega hablaremos de otro escenario en el cual la expresión null! es de utilidad.

奥克塔维奥

Octavio Hernandez

Desarrollador y consultor en tecnologías .NET. Microsoft C# MVP entre 2004 y 2010.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *