En noviembre de 2022 se lanzó .NET 7 y desde ese momento tenemos disponibles todas las novedades que trae C# 11. A continuación revisamos algunas de ellas.

Puedes encontrar el código fuente de los ejemplos en repositorio: https://github.com/fjvela/csharp-11

Raw strings

Hasta ahora la manera de poder definir cadenas de texto multilínea en C# es utilizar el prefijo @. Uno de los problemas que presenta es que si se indenta el texto la salida de este se verá afectada o si se utilizan comillas dobles es necesario escaparlas.

C# 11 incluirá una nueva manera de definir cadenas de texto, para ello deberemos utilizar como mínimo tres comillas """. Esto nos facilitará poder indentar cadenas de texto en nuestro código y evitar tener que escapar las comillas. En el caso de utilizar una cadena de texto interpolada (string interpolation - “Hello, I’m {{ name }}”), deberemos utilizar como mínimo dos dólares $$.

Definición de cadenas de texto multilínea en C#

    var myString = @"
          <element attr=""content""/>";

Resultado:

          <element attr="content"/>

Definición de cadenas de texto multilíneas en C# 11

    var myStringRaw = """
          <element attr="content"/>
          """;

Resultado:

<element attr="content"/>

Resultado ejecución

Definición de cadenas de texto interpoladas en C# 11

    var myJSONRawInterpolated = $$"""
        {
            "name": "{{name}}"
        }
        """;

Resultado:

{
    "name": "Javi Vela"
}

Resultado binario decompilado

A continuación puedes ver el código decompilado: Resultado binario decompilado, raw strings

List patterns

Nos permitirá comparar un patrón con un array o list de elementos, por lo que podríamos definir un método con los siguientes patrones:

    int CheckSwitch(int[] values) => values switch
    {
        [1, 2, .., 10] => 1,
        [1, 2] => 2,
        [1, _] => 3,
        [1, ..] => 4,
        [21] => 5,
        [21, _, ..] => 6,
        [33, _, 34, .., 44] => 7,
        [_, ..] => 50
    };

Los resultados de la ejecución son:

    Console.WriteLine(CheckSwitch(new[] { 1, 2, 10 }));                 // prints 1
    Console.WriteLine(CheckSwitch(new[] { 1, 2, 7, 3, 3, 10 }));        // prints 1
    Console.WriteLine(CheckSwitch(new[] { 1, 2 }));                     // prints 2
    Console.WriteLine(CheckSwitch(new[] { 1, 3 }));                     // prints 3
    Console.WriteLine(CheckSwitch(new[] { 1, 3, 5 }));                  // prints 4
    Console.WriteLine(CheckSwitch(new[] { 2, 5, 6, 7 }));               // prints 50
    Console.WriteLine(CheckSwitch(new[] { 21, 52, 63, 74, 5 }));        // prints 5
    Console.WriteLine(CheckSwitch(new[] { 21 }));                       // prints 6
    Console.WriteLine(CheckSwitch(new[] { 33, 0, 34, 1, 2, 3, 44 }));   // prints 7

También podemos utilizarlo con la sentencia if y obtener los valores del mismo:

if (numbers is [var first, _, _])
{
    Console.WriteLine($"The first element of a three-item list is {first}.");
}

if (numbers is [1, var second, _])
{
    Console.WriteLine($"The second element of a three-item list is {second}.");
}

Resultado binario decompilado

A continuación puedes ver el código decompilado: Resultado binario decompilado, list patterns

UTF-8 String literals

Si tu aplicación necesita cadenas de texto en formato UTF-8 (ej: necesita comunicarse con otras a través de protocolos HTTP), el sufijo u8 puede ahorrarte unas líneas de código.

Añadiendo el sufijo u8, automáticamente transforma la cadena de texto en un array de bytes en formato UTF-8.

Por defecto para convertir una cadena de texto en un array de bytes en UTF-8, necesitas:

    byte[] data = System.Text.Encoding.UTF8.GetBytes("Javi Vela");

Gracias al sufijo ‘u8’, simplificamos la conversión:

    byte[] data = "Javi Vela"u8.ToArray();

Resultado binario decompilado

A continuación puedes ver el código decompilado: Resultado binario decompilado, UTF-8 string literals

Newlines in string interpolation expressions

Versiones anteriores C# 11 no permiten el uso de saltos de línea en cadenas de texto interpoladas (string interpolation - “Hello, I’m {{ name }}”).

En el caso de utilizarlas, el compilador da un error como podemos ver en la siguiente imagen.

C# 10, error de compilación salto de líneas en un string interpolated

C# 11 nos permite añadir saldos de línea sin modificar el formato del texto:

    Console.WriteLine($"Hello {name
        }!!, How are you?");

Resultado binario decompilado

A continuación puedes ver el código decompilado: C# 11, compilación y ejecución de la aplicación

Generic math support

C# 11 incluye el soporte genérico de los datos matemáticos, permitiéndonos construir métodos genéricos como el siguiente:

    T AddAll<T>(T[] items) where T : INumber<T>
    {
        T result = T.Zero;
        foreach (var item in items)
            result += item;
        return result;
    }

    int[] numbers = new[] { 1, 5, 6, 9, 19};

    Console.WriteLine(AddAll(numbers));

Required members

Existe un nuevo modificador required que podemos añadir a nuestras propiedades para indicar que los constructores deben inicializar esa propiedad, en caso contrario nuestra aplicación no compilara.

El siguiente código muestra la inicialización de una clase pero no se ha inicializado la propiedad Name en el constructor por lo que tendremos un error de compilación.

var user = new User { };

Console.WriteLine($"Name: {user.Name}");

class User
{
    public required string Name { get; init; }
}

C# 11, error de compilación propiedad ‘Name’ no inicializada

File-local types

Se incluye en C# 11 un nuevo modificador de clases, file. Al indicar el modificador de acceso file estamos indicando que la visibilidad (scope) de la clase es dentro del mismo fichero.

C# 11 ejemplo clases modificador ‘file’

Resultado binario decompilado

A continuación puedes ver el código decompilado:

C# 11 liberia decompilada, las clases con modificador ‘file’ no están

Auto-default-structs

Versiones anteriores a C# 11 obligan a inicializar todas las propiedades de un struct. C# 11 inicializará automáticamente todas las propiedades que no han sido inicializadas.

En la siguiente imagen podemos ver como automáticamente se ha inicializado la propiedad ‘Y’ con el valor por defecto de su tipo, en este caso cero.

C# 11, definición de un struct. La propiedad Y no está inicializada y el compilador la ha inicializado a cero

Resultado binario decompilado

A continuación podemos ver la librería decompilada y como el compilador ha añadido el código necesario para inicializar la propiedad por nosotros: C# 11, librería decompilada. Por defecto el valor de Y en el constructor es cero

Referencias