Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 27 de septiembre de 2022
.NET

En un vídeo del canal de Nick Chapsas, al que por cierto os recomiendo suscribiros, he descubierto que .NET 7 introducirá un mecanismo para "decorar" parámetros, propiedades y miembros de tipo string de forma que podamos aportar información sobre el tipo de contenido que esperan almacenar.

Para que lo entendáis mejor, observad el siguiente ejemplo, una función que recibe un mensaje y un formato de fecha, y que escribe por consola la fecha actual en el formato indicado seguido del mensaje.

void Log(string message, string dateFormat)
{
    Console.WriteLine(DateTime.UtcNow.ToString(dateFormat) + " - " + message);
}

Log("Hello!", "dd/MM/yyyy hh:mm");

Desde el punto de vista del consumidor de la función Log(), gracias a las ayudas del IDE podremos deducir que el segundo parámetro de tipo string, llamado dateFormat, debería ser un formato de fecha válido en .NET. Sin embargo, el entorno de desarrollo no podrá ofrecer ningún tipo de ayuda a la hora de codificar la llamada ni detectar si se producen errores, pues no dispone de información suficiente sobre el tipo de contenido esperado en la cadena de texto que se le suministra.

Esto es lo que viene a solucionar el nuevo atributo [StringSyntax] de .NET 7. Gracias a él, podemos cualificar parámetros, propiedades o miembros de tipo string, informando al IDE del contenido esperado para ellos y, como consecuencia, el entorno podría facilitar al desarrollador ayudas como colorización, autocompletado o chequeo en tiempo de compilación.

Para verlo, modifiquemos ligeramente el ejemplo anterior, decorando el parámetro dateFormat con este atributo, en el que indicamos que el contenido esperado para él sería la especificación de un formato de fecha:

void Log(string message, [StringSyntax(StringSyntaxAttribute.DateTimeFormat)]string dateFormat)
{
    Console.WriteLine(DateTime.UtcNow.ToString(dateFormat) + " - " + message);
}

Log("Hello!", "dd/MM/yyyy hh:mm");

De esta forma, mientras el desarrollador implementa la llamada a Log(), el entorno podrá mostrar una ayuda como la que se muestra en la siguiente captura de pantalla:

Intellisense en Visual Studio mostrando un desplegable con formatos de fecha disponibles

Veamos otro ejemplo. El siguiente código muestra una función de deserialización que admite una cadena JSON:

T? Deserialize<T>([StringSyntax(StringSyntaxAttribute.Json)] string json)
{
    return JsonSerializer.Deserialize<T>(json);
}

Deserialize<Person>("{ this is not json }");

Mientras vamos implementando la llamada a Deserialize(), veremos cómo los valores JSON inválidos son subrayados como error y en la lista de warnings podemos ver los errores detectados:

Visual Studio detectando que estamos introduciendo un valor JSON inválido

Por supuesto, podemos combinar esta característica con los nuevos raw string literals de C# 11 para introducir JSON en una cadena de forma totalmente natural, y tendremos las mismas ayudas y validaciones:

var name = "John";
var age = 33;

var person = Deserialize<Person>($$"""
    { 
        "name": "{{name}}",
        "age": {{age}}
    }
    """);

El atributo StringSyntaxAttribute puede ser aplicado a parámetros, campos y propiedades de clases, por lo que podríamos usarlo como en el siguiente ejemplo:

public class Person
{
    public string Name { get; set; }
    ...
    [StringSyntax(StringSyntaxAttribute.Json)]
    public string ExtendedProperties { get; set; }
}

Por último, indicar que las sintaxis admitidas en la clase StringSyntaxAttribute son las siguientes, aunque a día de hoy (.NET 7 RC-1) sólo algunas de ellas son interpretadas correctamente desde los entornos de desarrollo:

  • StringSyntaxAttribute.Composite, para formatos disponibles en string.Format().
  • StringSyntaxAttribute.DateOnlyFormat
  • StringSyntaxAttribute.DateTimeFormat
  • StringSyntaxAttribute.EnumFormat
  • StringSyntaxAttribute.GuidFormat
  • StringSyntaxAttribute.Json
  • StringSyntaxAttribute.NumericFormat
  • StringSyntaxAttribute.Regex
  • StringSyntaxAttribute.TimeOnlyFormat
  • StringSyntaxAttribute.TimeSpanFormat
  • StringSyntaxAttribute.Uri
  • StringSyntaxAttribute.Xml

¿Y es posible usarlo en versiones anteriores a .NET 7?

Pues sí, en versiones anteriores de .NET existía algo en la práctica bastante parecido, aunque se implementaba de forma distinta. En lugar de utilizar un atributo, se puede introducir un comentario en el código para indicar la sintaxis de la cadena de texto que aparece justo a continuación, como en los siguientes ejemplos:

// El IDE mostrará dos warnings con este código, 
// porque ninguna de las cadenas es un JSON válido:

// Ejemplo 1: comentario de línea completa
// language=json
var json1 = "{ 'name': 'John }";

// Ejemplo 2: comentario en línea
var json2 = /*language=json*/"this is not a valid json";

No he conseguido encontrar documentación oficial sobre este mecanismo, pero de momento he comprobado que las siguientes opciones funcionan en proyectos .NET Framework 4.7 y versiones anteriores de .NET Core:

// language=regex
// language=datetimeformat
// language=json

La ventaja de usar este enfoque, que también está disponible en .NET 7, es que podemos emplearlo en cualquier momento, no únicamente en parámetros, propiedades o campos. En el siguiente ejemplo podéis ver cómo usar el comentario sobre la definición de una cadena JSON:

// language=json
var json = $$"""
    { 
        "name": "{{name}}",
        "age": {{age}}
    }
    """);
var person = Deserialize<Person>(json);

¡Y esto es todo! La verdad es que, aunque no sea una característica de las que vayamos a usar todos los días, sí es interesante saber que se está trabajando para facilitarnos esos escenarios en los que nuestros objetos string son algo más que simples cadenas de texto, y que probablemente este mecanismo siga extendiéndose para soportar otras sintaxis como rutas de archivos, SQL, C#, JavaScript u otros lenguajes, etc.

Publicado en Variable not found.