0

.Net 6 Preview 7

El pasado 10 de agosto se lanzó la última vista previa antes de entrar en los Release Candidate (RC). Para esta nueva preview el equipo ha invertido un gran esfuerzo obteniendo el un gran conjunto de características. Desde ahora en adelante , el equipo de .net  se centrará en llevar todas las características a una calidad de excelencia para que .NET 6 esté listo para su uso.

Podemos ‎‎descargar .NET 6 Preview 7‎‎ para Linux, macOS y Windows.‎

‎.NET SDK: plantillas de proyecto de C# modernizadas‎

Se actualizaron ‎‎las plantillas del SDK de .NET‎‎ para usar características y patrones más recientes del lenguaje C#. ‎En las nuevas plantillas se utilizan las siguientes características de idioma:‎

  • ‎Declaraciones de nivel superior‎
  • ‎async Principal‎
  • ‎Directivas de uso global (a través de valores predeterminados controlados por SDK)‎
  • ‎Espacios de nombres con ámbito de archivo‎
  • ‎Nuevas expresiones con tipo de destino‎
  • ‎Tipos de referencia anulables‎

‎Plantilla de consola‎

Esta es la que más cantidad de cambios tuvo. Ahora ‎console es (efectivamente) una línea única en virtud de declaraciones de alto nivel y directivas de uso global.

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

‎La versión .NET 5 de la misma plantilla incluye varias líneas que proporcionan la estructura previamente necesaria incluso para una sola línea de código real.‎

using System;

namespace Company.ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");
        }
    }
}

‎El archivo de proyecto de la plantilla ‎console también ha cambiado para habilitar la característica ‎‎de tipos de referencia anulables‎‎, como puede ver en el ejemplo siguiente.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

‎Otras plantillas también habilitan la anulabilidad, los usos globales implícitos y los espacios de nombres con ámbito de archivo, incluidos ASP.NET Core y class Library.‎

‎ASP.NET plantilla web‎

‎La plantilla ‎web también se redujo manera similar en líneas de código, utilizando las mismas características.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.MapGet("/", () => "Hello World!");

app.Run();

‎ASP.NET plantilla MVC‎

Similar a la anterior. En este caso, se ha fusionado y en un solo archivo program.cs y startup.cs, creando una simplificación adicional.

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Home/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

‎Compatibilidad de plantillas‎

Si quieres ver los problemas de compatibilidad con las nuevas plantillas puedes ver los siguientes links:

‎Bibliotecas: API de reflexión para información de anulabilidad‎

‎Los tipos de referencia anulables‎‎ son una característica importante para escribir código confiable. Funciona muy bien para escribir código, pero no (hasta ahora) para inspeccionar o depurar. ‎‎Las nuevas API de Reflection‎‎ permiten determinar la naturaleza de anulabilidad de los parámetros y los valores devueltos para un método determinado. 

‎En los ejemplos siguientes se muestra el uso de las nuevas API para un par de escenarios diferentes.‎

‎Obtención de información de anulabilidad de nivel superior‎

Imaginemos que estamos implementando un serializador. Con estas nuevas API, el serializador puede comprobar si una propiedad determinada se puede establecer en :‎null

private NullabilityInfoContext _nullabilityContext = new NullabilityInfoContext();

private void DeserializePropertyValue(PropertyInfo p, object instance, object? value)
{
    if (value is null)
    {
        var nullabilityInfo = _nullabilityContext.Create(p);
        if (nullabilityInfo.WriteState is not NullabilityState.Nullable)
        {
            throw new MySerializerException($"Property '{p.GetType().Name}.{p.Name}'' cannot be set to null.");
        }
    }

    p.SetValue(instance, value);
}

‎Obtención de información de anulabilidad anidada‎

‎La anulabilidad tiene un tratamiento especial para los objetos que pueden (formalmente) contener otros objetos, como matrices y tuplas. Por ejemplo, podemos especificar que un objeto de matriz (como variable o como parte de una firma de miembro de tipo) no debe ser nulo, pero que los elementos pueden ser nulos o viceversa. Este nivel adicional de especificidad se puede inspeccionar con las nuevas API de Reflection, como se muestra en el siguiente ejemplo.‎

class Data
{
    public string?[] ArrayField;
    public (string?, object) TupleField;
}
private void Print()
{
    Type type = typeof(Data);
    FieldInfo arrayField = type.GetField("ArrayField");
    FieldInfo tupleField = type.GetField("TupleField");

    NullabilityInfoContext context = new ();

    NullabilityInfo arrayInfo = context.Create(arrayField);
    Console.WriteLine(arrayInfo.ReadState);        // NotNull
    Console.WriteLine(arrayInfo.Element.State);    // Nullable

    NullabilityInfo tupleInfo = context.Create(tupleField);
    Console.WriteLine(tupleInfo.ReadState);                      // NotNull
    Console.WriteLine(tupleInfo.GenericTypeArguments [0].State); // Nullable
    Console.WriteLine(tupleInfo.GenericTypeArguments [1].State); // NotNull
}

Bibliotecas: ZipFile respeta los permisos de archivos Unix‎

‎La clase System.IO.Compression.ZipFile ahora captura los permisos de archivos Unix durante la creación y establece permisos de archivos al extraer archivos zip en sistemas operativos similares a Unix. Este cambio permite que los archivos ejecutables se disparen a través de un archivo zip, lo que significa que ya no tiene que modificar los permisos de archivo para que los archivos sean ejecutables después de extraer un archivo zip. También respeta los permisos de lectura/escritura para ‎user, group y other  también.

‎Si un archivo zip no contiene permisos de archivo (porque se creó en Windows o utilizando una herramienta que no capturó los permisos, como una versión anterior de .NET), los archivos extraídos obtienen los permisos de archivo predeterminados, al igual que cualquier otro archivo recién creado.‎

‎Los permisos de archivo Unix también funcionan con otras herramientas de archivo zip, que incluye:‎

‎Vista previa de la característica de .NET 7: matemáticas genéricas‎

‎Una de esas características que estamos previsualizando en .NET 6 son los miembros de la interfaz abstracta estática. Estos le permiten definir métodos abstractos estáticos (incluidos los operadores) en las interfaces. Por ejemplo, ahora es posible implementar métodos genéricos algebraicos. Para algunas personas, esta característica será la mejora absolutamente destacada que estamos entregando este año. Es quizás la nueva capacidad de sistema de tipo más importante desde .‎Span<T>

‎El siguiente ejemplo toma un .‎IEnumerable<T> y es capaz de sumar todos los valores debidos al estar restringido a TINumber<T> , posiblemente un INumber<int>.

public static T Sum<T>(IEnumerable<T> values)
    where T : INumber<T>
{
    T result = T.Zero;

    foreach (var value in values)
    {
        result += value;
    }

    return result;
}

‎Esto funciona porque ‎‎INumber‎‎<‎‎T‎‎>‎‎ define ‎‎varias sobrecargas de operadores (estáticas‎‎) que deben satisfacer los implementadores de interfaz. ‎‎IAdditionOperators es quizás‎‎ la nueva interfaz más fácil de entender, de la que se deriva.‎INumber<T>

‎Todo esto está impulsado por una nueva característica que permite que los miembros sean declarados en interfaces. Esto permite que las interfaces expongan operadores y otros métodos estáticos, como o , y que se implementen mediante un tipo derivado.

‎Bibliotecas: API de NativeMemory‎

‎Agregamos nuevas ‎‎API de asignación de memoria nativa expuestas‎‎ a través de ‎System.Runtime.InteropServices.NativeMemory. Estas API representan equivalentes a las API malloc, free, realloc y calloc, C también incluyen API para realizar asignaciones alineadas.

‎Bibliotecas: Notificaciones de serialización System.Text.Json‎

‎El serializador System.Text.Json ahora expone notificaciones como parte de las operaciones de (des)serialización. Es util para valores predeterminados y validación. Para usarlos, implemente una o más de las interfaces IJsonOnDeserialized, IJsonOnDeserializing, IJsonOnSerialized  o IJsonOnSerializing  dentro del espacio de nombres System.Text.Json.Serialization.

Veamos un ejemplo que valida durante ambos .‎JsonSerializer.Serialize() y JsonSerializer.Deserialize() para  garantizar que una propiedad FirstName no lo sea null.

 public class Person : IJsonOnDeserialized, IJsonOnSerializing
  {
      public string FirstName{ get; set; }

      void IJsonOnDeserialized.OnDeserialized() => Validate(); // Call after deserialization
      void IJsonOnSerializing.OnSerializing() => Validate(); // Call before serialization

      private void Validate()
      {
          if (FirstName is null)
          {
              throw new InvalidOperationException("The 'FirstName' property cannot be 'null'.");
          }
      }
  }

‎Anteriormente, necesitaría implementar un convertidor personalizado para lograr esta funcionalidad.‎

‎Bibliotecas: ordenación de propiedades de serialización System.Text.Json‎

‎Se ha agregado la capacidad de controlar el orden de serialización de las propiedades, con . Un entero especifica el orden. Los enteros más pequeños se serializan primero; las propiedades que no tienen ningún atributo tienen un valor de ordenación predeterminado de System.Text.Json.Serialization.JsonPropertyOrderAttribute.

‎Aquí hay un ejemplo que especifica que JSON debe serializarse en el orden:‎Id, City, FirstName, LastName.

public class Person
{
    public string City { get; set; } // No order defined (has the default ordering value of 0)

    [JsonPropertyOrder(1)] // Serialize after other properties that have default ordering
    public string FirstName { get; set; }

    [JsonPropertyOrder(2)] // Serialize after FirstName
    public string LastName { get; set; }

    [JsonPropertyOrder(-1)] // Serialize before other properties that have default ordering
    public int Id { get; set; }
}

‎Anteriormente, el orden de serialización estaba determinado por un orden de reflexión que no era determinista ni daba como resultado un orden deseado específico.‎

‎Bibliotecas: JSON «write raw» con System.Text.Json.Utf8JsonWriter‎

‎En ocasiones en las que necesita ‎‎integrar JSON «sin procesar» al escribir cargas útiles JSON‎‎ con Utf8JsonWriter.‎

‎Por ejemplo:‎

  • Tenemos una secuencia deliberada de bytes que quiero escribir en el cable, y sé lo que estoy haciendo (como se muestra en el siguiente ejemplo).‎
  • Tenemos un blob que creo que representa el contenido JSON y que quiero envolver, y necesito asegurarme de que el sobre y su contenido interno permanezcan bien formados.‎
JsonWriterOptions writerOptions = new() { WriteIndented = true, };

using MemoryStream ms = new();
using UtfJsonWriter writer = new(ms, writerOptions);

writer.WriteStartObject();
writer.WriteString("dataType", "CalculationResults");

writer.WriteStartArray("data");

foreach (CalculationResult result in results)
{
    writer.WriteStartObject();
    writer.WriteString("measurement", result.Measurement);

    writer.WritePropertyName("value");
    // Write raw JSON numeric value using FormatNumberValue (not defined in the example)
    byte[] formattedValue = FormatNumberValue(result.Value);
    writer.WriteRawValue(formattedValue, skipValidation: true);

    writer.WriteEndObject();
}

writer.WriteEndArray();
writer.WriteEndObject();

‎La siguiente es una descripción de lo que el código anterior, en particular, está haciendo. Para el rendimiento, omite los puntos/valores decimales cuando el número es entero, como . La razón es que escribir menos bytes es bueno para perf. En algunos escenarios, puede ser importante conservar los valores decimales porque el consumidor trata los números sin decimales como enteros, de lo contrario como dobles. Este nuevo modelo de «valor bruto» le permite tener ese nivel de control donde lo necesite.‎FormatNumberValueSystem.Text.Json1.0

Bibliotecas: sobrecargas de flujo sincrónico en ‎JsonSerializer

Se han agregado ‎‎nuevas API síncronas‎‎ para ‎JsonSerializer serializar y deserializar datos JSON hacia/desde una secuencia. Puede ver eso demostrado en el siguiente ejemplo.

using MemoryStream ms = GetMyStream();
MyPoco poco = JsonSerializer.Deserialize<MyPoco>(ms);

‎Estas nuevas API síncronas incluyen sobrecargas que son compatibles y utilizables con el nuevo ‎‎generador de origen System.Text.Json‎‎, mediante la aceptación o instancias.‎JsonTypeInfo<T>JsonSerializerContext

‎Bibliotecas: System.Text.Json.Nodes.JsonNode‎ ha eliminado la compatibilidad ‎dynamic

‎Se ha quitó la compatibilidad con el tipo ‎‎dinámico‎‎ de C# en JsonSerializer. Agregamos soporte en Preview 4, pero luego decidimos ser una mala opción de diseño, lo que incluye convertirlo en una dependencia requerida del tipo JsonNode.

Bibliotecas: Propagadores System.Diagnostics‎

Se ha mejorado el soporte para ‎‎OpenTelemetry‎‎ en los últimos años. Uno de los aspectos clave para habilitar un gran soporte es garantizar que todos los componentes que necesitan participar en la producción de telemetría produzcan encabezados de red en el formato correcto. Es realmente difícil hacer eso, especialmente a medida que cambia la especificación de OpenTelemetry. OpenTelemetry define el concepto ‎‎de propagación‎‎ para ayudar con esta situación. Estamos en el proceso de adoptar la ‎‎propagación‎‎ para habilitar un modelo general para la personalización del encabezado.‎

‎Contexto sobre los conceptos más amplios:‎

‎El código siguiente muestra el enfoque general para usar la propagación.‎

DistributedContextPropagator propagator = DistributedContextPropagator.Current;
propagator.Inject(activity, carrier, (object theCarrier, string fieldName, string value) =>
{
   // Extract the context from the activity then inject it to the carrier.
});

‎También puede optar por utilizar un propagador diferente.‎

‎También puede optar por utilizar un propagador diferente.‎
// Set the current propagation behavior to not transmit any distributed context information in outbound network messages.
DistributedContextPropagator.Current = DistributedContextPropagator.CreateNoOutputPropagator();

‎La clase abstracta DistributedContextPropagator  determina si y cómo se codifica y decodifica la información de contexto distribuida a medida que atraviesa la red. La codificación se puede transportar a través de cualquier protocolo de red que admita pares de cadenas clave-valor. DistributedContextPropagator  inyectar valores y extraer valores de portadoras como pares de cadenas clave/valor. Al agregar soporte para propagadores, hemos habilitado dos cosas:‎

  • ‎Ya no es necesario utilizar los encabezados ‎‎TraceContext del W3C‎‎. Puede escribir un propagador personalizado (es decir, usar sus propios nombres de encabezados, incluido no enviarlos en absoluto) sin que las bibliotecas HttpClient, ASP.NET Core tengan un conocimiento a priori de este formato personalizado.‎
  • ‎Si implementa una biblioteca con un transporte personalizado (por ejemplo, cola de mensajes), ahora puede admitir varios formatos de cable siempre que admita el envío y la recepción de un mapa de texto (por ejemplo, ‎Dictionary<string, string>)

‎La mayoría del código de la aplicación no necesita usar directamente esta característica, sin embargo, es probable que lo vea en una pila de llamadas si usa OpenTelemetry. Algunos códigos de biblioteca querrán participar en este modelo si se preocupan por el rastreo y la causalidad.‎

‎Bibliotecas: patrones de llamada simplificados para operaciones criptográficas‎

‎Las rutinas de cifrado y descifrado de .NET se diseñaron en torno a la transmisión, sin un concepto real para definir cuándo la carga útil ya está en la memoria. Los nuevos métodos Encrypt- y Decrypt- aceleran el caso que ya está en memoria, y están destinados a proporcionar claridad a la persona que llama y al revisor de código. Además, admiten la lectura y la escritura en tramos.‎SymmetricAlgorithm

‎Los nuevos métodos simplificados ofrecen un enfoque sencillo para el uso de API criptográficas:‎

private static byte[] Decrypt(byte[] key, byte[] iv, byte[] ciphertext)
{
    using (Aes aes = Aes.Create())
    {
        aes.Key = key;

        return aes.DecryptCbc(ciphertext, iv);
    }
}

Estos nuevos nuevos métodos Encrypt y Decrypt, solo se utiliza la propiedad key de la instancia SymmetricAlgorithm. El nuevo método DecryptCbc admite la elección del algoritmo de relleno, pero PKCS#7 se usa con CBC con tanta frecuencia que es un argumento predeterminado. Si te gusta la claridad, simplemente especificarla:‎

private static byte[] Decrypt(byte[] key, byte[] iv, byte[] ciphertext)
{
    using (Aes aes = Aes.Create())
    {
        aes.Key = key;

        return aes.DecryptCbc(ciphertext, iv, PaddingMode.PKCS7);
    }
}

‎Puede ver que el patrón existente, con .NET 5, requería significativamente más plomería para el mismo resultado.‎

private static byte[] Decrypt(byte[] key, byte[] iv, byte[] ciphertext)
{
    using (Aes aes = Aes.Create())
    {
        aes.Key = key;
        aes.IV = iv;

        // These are the defaults, but let's set them anyways.
        aes.Padding = PaddingMode.PKCS7;
        aes.Mode = CipherMode.CBC;

        using (MemoryStream destination = new MemoryStream())
        using (ICryptoTransform transform = aes.CreateDecryptor())
        using (CryptoStream cryptoStream = new CryptoStream(destination, transform, CryptoStreamMode.Write))
        {
            cryptoStream.Write(ciphertext, 0, ciphertext.Length);
            cryptoStream.FlushFinalBlock();
            return destination.ToArray();
        }
    }
}

Conclusión

‎Esta es la última versión preview, a unos pocos pasos de la versión final. Ahora el equipo se focalizará en las mejoras para la primera release candidate.

Fernando Sonego

Deja una respuesta

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