Este 12 de octubre se ha lanzado la segunda y última release candidate programada antes del lanzamiento oficial próximamente en noviembre. Hay características nuevas en la versión, que solo se unen completamente cerca de la versión final.
Puede descargar .NET 6 Release Candidate 2 para Linux, macOS y Windows.
- Instaladores y binarios
- Imágenes de contenedores
- Paquetes de Linux
- Notas
- API diff
- Problemas conocidos
- Rastreador de problemas de GitHub
.NET 6 RC2 se ha probado y es compatible con Visual Studio 2022 Preview 5, que también se publica el mismo dia. .NET 6 se admitirá con Visual Studio 2022 y no con Visual Studio 2019. Del mismo modo, será compatible con MSBuild 17.x y no con 16.x. Si desea usar .NET 6, deberá actualizar a Visual Studio 2022.
Visual Studio 2022 para Mac actualmente NO es compatible con .NET 6 RC2. Estan en busqueda de resolverlo.
C# 10
C# 10 es una parte importante de la versión .NET 6. En su mayor parte, C# 10 es una evolución adicional de los conceptos y capacidades existentes, como records y patterns. También incluye características, y espacios de nombres con ámbito de archivo, que le ayudan a simplificar el código y escribir menos repetitivo.
Record structs
C# 10 agrega compatibilidad con estructuras de registro. Esta nueva característica es similar a los registros de C# 9 (basados en clases), con algunas diferencias clave. En su mayor parte, se han agregado estructuras de registro para mayor integridad para que las estructuras puedan disfrutar de los mismos beneficios de registro que las clases. Sin embargo, el equipo no simplemente estructuró los registros, sino que decidió alinear los registros de estructura con ValueTuple tanto como los registros de clase. Como resultado de ese enfoque de diseño, las propiedades de estructura de registro son mutables de forma predeterminada, mientras que las propiedades de clase de registro son inmutables. Sin embargo, puede declarar un readonly record struct, que es inmutable y coincide con la semántica record class.
En un nivel alto, las estructuras de registro no reemplazan a las clases de registro, ni fomentamos la migración de clases de registro a estructuras de registro. La guía para usar clases vs structs se aplica igualmente a las clases de registro y a las estructuras de registro. Dicho de otra manera, la elección entre clases y estructuras debe hacerse antes de elegir usar registros.
Battery battery = new("CR2032", 0.235, 100); WriteLine(battery); while (battery.RemainingCapacityPercentage > 0) { battery.RemainingCapacityPercentage--; } WriteLine(battery); public record struct Battery(string Model, double TotalCapacityAmpHours, int RemainingCapacityPercentage);
Este ejemplo de una estructura de registro produce el siguiente resultado.
Battery { Model = CR2032, TotalCapacityAmpHours = 0.235, RemainingCapacityPercentage = 100 } Battery { Model = CR2032, TotalCapacityAmpHours = 0.235, RemainingCapacityPercentage = 0 }
Como ya se ha dicho, la diferencia más obvia es que las propiedades de estructura de registro son mutables de forma predeterminada. Esa es la diferencia clave más allá de ser estructuras y la sintaxis record struct. También he reescrito el ejemplo con un readonly record struct , de la siguiente manera.
Battery battery = new("CR2032", 0.235, 100); WriteLine(battery); while (battery.RemainingCapacityPercentage > 0) { Battery updatedBattery = battery with {RemainingCapacityPercentage = battery.RemainingCapacityPercentage - 1}; battery = updatedBattery; } WriteLine(battery); public readonly record struct Battery(string Model, double TotalCapacityAmpHours, int RemainingCapacityPercentage);
Verá que es casi idéntico al ejemplo de registro (de clase) que publiqué para C# 9.
Revisemos los registros de C# 9. Proporcionan una sintaxis concisa para definir clases orientadas a datos similares a estructuras. Se sesgan a la inmutabilidad al tiempo que ofrecen una sintaxis concisa (expresiones) para una copia fácil de usar. Podría haber sorprendido a la gente que comenzamos con clases para una característica que pretende ser similar a una estructura. La mayoría de las veces, los desarrolladores usan clases sobre estructuras, debido a que pasan por referencia en lugar de semántica de valores. Las clases son la mejor opción en la mayoría de los casos y más fáciles de usar.with
Los registros de estructura son muy similares a los registros de clase:
- Utilizan la misma sintaxis (con la excepción de struct o class en la definición).
- Permiten personalizar las definiciones de miembros (nuevas en C# 10) para usar campos sobre (de forma predeterminada) miembros de propiedad.
- Permiten personalizar el comportamiento del miembro, las propiedades de uso init o mutables.
- Apoyan las expresiones. De hecho, a partir de C# 10, todos los tipos struct admiten expresiones with.
Los registros de estructura difieren de los registros de clase:
- Las estructuras de registro se definen con o .record structreadonly record struct
- Las clases de registro se definen con o .recordrecord class
- Las propiedades de estructura de registro son mutables (/) de forma predeterminada.getset
- Las propiedades de la clase de registro son inmutables (/) de forma predeterminada.getinit
En virtud de la semántica del valor de paso, las estructuras no se benefician de la inmutabilidad casi tanto como las clases. Por ejemplo, si una estructura es una clave en un diccionario, esa clave (copia de la estructura) es inmutable y las búsquedas se realizan a través de la igualdad. Al mismo tiempo, el equipo de diseño vio que se puede considerar como una estructura de registro anónima y que las estructuras de registro se pueden ver como un crecimiento de . Eso solo funciona si las estructuras de registro abarcan la mutabilidad y los campos de soporte, que es exactamente lo que el equipo decidió hacer. Además, abrazar la mutabilidad es un reflejo de que las estructuras y las clases son diferentes.
Si prefiere el comportamiento inmutable con estructuras de registro, puede tenerlo agregando la palabra clave readonly.
Mirando las estructuras en general, la funcionalidad clave es común:
- Las comprobaciones de igualdad son las mismas para todas las estructuras, proporcionadas por el tiempo de ejecución.
- Todas las estructuras pueden usar expresiones with para crear copias no mutantes, lo cual es nuevo en C# 10.
Global usings
global using le permite especificar un espacio de nombres que desea que esté disponible en todos los archivos de origen, como si se hubiera declarado en cada uno. También se puede utilizar con using static y aliasing. Esta característica permite que un conjunto común de declaraciones esté disponible y, por extensión, que muchas líneas ya no sean necesarias. Esto es más relevante para los espacios de nombres de la plataforma, pero se puede usar para cualquier espacio de nombres. La sintaxis siguiente se puede utilizar para las distintas formas:
- global using System;
- global using static System.Console;
- global using E = System.Environment;
Estas declaraciones solo necesitan ser declaradas una vez en su compilación para que sean efectivas a lo largo de la misma. Hay cuatro patrones para declarar .global using
- En Program.cs, haga que sus instrucciones raíz sean globales para todo el programa actualizándose a global using
- En un archivo GlobalUsings.cs (o algún nombre similar), administre de forma centralizada todos sus global using estados de cuenta.
- En el archivo de proyecto, con la sintaxis siguiente.
- En el archivo de proyecto, habilite las instrucciones de plataforma predeterminadas (para el SDK de MSBuild en el que se basa la aplicación), con la sintaxis siguiente.
La siguiente sintaxis de MSBuild se puede utilizar en lugar de archivos .cs, en un <ItemGroup> (utilizando análogos de los ejemplos anteriores).
- <Using Include=»System.Console» Static=»True»/>
- <Using Include=»System.Environment» Alias=»E»/>
Puede habilitar instrucciones implícitas definidas using por la plataforma en un archivo <PropertyGroup>.
- <ImplicitUsings>enable</ImplicitUsings>
El uso implícito difiere según MSBuild SDK. Microsoft.NET.Sdk define un conjunto básico y otros SDK definen otros adicionales.
Si utiliza las capacidades de MSBuild, puede ver el resultado efectivo en un obj archivo generado en el directorio, como se muestra a continuación.
PS C:\Users\rich\app> type .\app.csproj | findstr Using <ImplicitUsings>enable</ImplicitUsings> <Using Include="System.Console" Static="True"/> <Using Include="System.Environment" Alias="E"/> PS C:\Users\rich\app> type .\obj\Debug\net6.0\app.GlobalUsings.g.cs // <auto-generated/> global using global::System; global using global::System.Collections.Generic; global using global::System.IO; global using global::System.Linq; global using global::System.Net.Http; global using global::System.Threading; global using global::System.Threading.Tasks; global using E = global::System.Environment; global using static global::System.Console;
Declaración de espacio de nombres con ámbito de archivo
Las declaraciones de espacio de nombres con ámbito de archivo es otra característica de C# 10 que tiene como objetivo reducir la sangría y el recuento de líneas.
La sintaxis de esta característica es la siguiente:
namespace Foo;
Es una alternativa a la sintaxis tradicional de tres líneas:1
namespace Foo { }
La sintaxis de tres líneas se puede anidar. La sintaxis de una línea no admite el anidamiento. Solo puede haber una declaración con ámbito de archivo por archivo. Debe preceder a todos los tipos definidos en el archivo, al igual que la sintaxis de tres líneas.
const y cadenas interpoladas
Las cadenas interpoladas ahora se pueden asignar a las variables. Las cadenas interpoladas son intuitivas de usar y leer, y deben ser utilizables en todas partes. Ahora se pueden usar con siempre que los valores de marcador de posición también sean constantes.constconst
En el ejemplo siguiente se muestra un posible caso de uso:
const string Bar = "Bar"; const string DoubleBar = $"{Bar}_{Bar}"; WriteLine(DoubleBar);
Patrones de propiedades extendidos
Ahora puede hacer referencia a propiedades o campos anidados dentro de un patrón de propiedades. Por ejemplo, el siguiente patrón ahora es legal:
{ Prop1.Prop2: pattern }
Anteriormente, tendrías que usar un formulario más detallado:
{ Prop1: { Prop2: pattern } }
Puede ver esta forma más compacta utilizada en el siguiente ejemplo, por ejemplo con Reading.
List<Status> statuses = new() { new(Category.Normal, new(20, false, 20)), new(Category.Warning, new(20, false, 60)), new(Category.Danger, new(20, true, 60)), new(Category.Danger, new(100, false, 20)) }; foreach (Status status in statuses) { string message = status switch { {Category: Category.Normal} => "Let the good times roll", {Category: Category.Warning, Reading.PM25: >50 and <100} => "Check the air filters", {Reading.PM25: >200 } => "There must be a fire somewhere. Don't go outside.", {Reading.SmokeDetected: true } => "We have a fire!", {Category: Category.Danger} => "Something is badly wrong", _ => "Unknown status" }; Console.WriteLine(message); } record struct Reading(int Temperature, bool SmokeDetected, int PM25); record struct Status(Category Category, Reading Reading); enum Category { Normal, Warning, Danger }
.NET SDK: plantillas de proyecto de C# modernizadas
Modernizamos las plantillas del SDK de .NET en la Preview 7, utilizando las características y patrones de C# más recientes. Como parte de la actualización inicial de las plantillas, habilitamos el uso implícito de forma predeterminada (también conocido como exclusión voluntaria) para proyectos de .NET 6 () (incluso si actualizó una aplicación de .NET 5 a .NET 6). Eso ha cambiado. Hemos actualizado el SDK para que todas las nuevas características sean opt-in.
También hubo comentarios de que a algunas personas no les gustaba el nuevo archivo simplificado, con declaraciones de nivel superior. Hemos realizado mejoras en las declaraciones de nivel superior en respuesta y hemos seguido usándolas para plantillas.
En las nuevas plantillas se utilizan las siguientes características de idioma:
- async Principal
- Declaraciones de nivel superior
- Nuevas expresiones con tipo de destino
- Directivas de uso global
- Espacios de nombres con ámbito de archivo
- Tipos de referencia anulables
Un tema común en estas características es que reducen el ruido y aumentan la señal cuando está mirando su código en un editor de código.
Plantilla de consola
// See https://aka.ms/new-console-template for more information Console.WriteLine("Hello, World!")
Archivo del proyecto:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net6.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> </Project>
Aunque la plantilla de consola es mucho más mínima que su contraparte de .NET 5, no hay reducción en la funcionalidad. También puede ver que ahora es una función de suscripción y está habilitada en las plantillas.ImplicitUsings
Plantillas web
La plantilla web es igualmente mínima:
var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.MapGet("/", () => "Hello World!"); app.Run();
La plantilla webapi se asigna más estrechamente a las aplicaciones típicas de ASP.NET Core. Notará rápidamente que la división entre y ha sido eliminada. ya no es necesario.
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
El archivo de proyecto de la plantilla webapi es similar al anterior.
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net6.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> </PropertyGroup> <ItemGroup> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.5" /> </ItemGroup> </Project>
Puede ver que los espacios de nombres de ámbito de archivo se utilizan para el rendimiento de este ejemplo, incluido en :WeatherForecast.cs
namespace webapi; public class WeatherForecast { public DateTime Date { get; set; } public int TemperatureC { get; set; } public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); public string? Summary { get; set; } }
Target-type se utiliza con:newWeatherForecastController.cs
private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
En la plantilla mvc, puede ver el uso de anotaciones anulables y un método con cuerpo de expresión.
namespace webmvc.Models; public class ErrorViewModel { public string? RequestId { get; set; } public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); }
Plantillas de formularios Windows Forms
La plantilla de formularios Windows Forms también se ha actualizado. Incluye la mayoría de las otras mejoras cubiertas con las otras plantillas, aunque notablemente no declaraciones de nivel superior.
namespace winforms; static class Program { /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { ApplicationConfiguration.Initialize(); Application.Run(new Form1()); } }
Puede ver el uso de la instrucción de una línea y la falta de instrucciones a nivel de plataforma debido a la habilitación de usos implícitos.
Las plantillas de WPF no se han actualizado como parte de la versión.
Usos implícitos
Comencemos con usos implícitos. Cuando está habilitado, cada Sdk agrega su propio conjunto de instrucciones de uso implícitas.
Como se demostró anteriormente, el Microsoft.NET.Sdk agrega varias declaraciones global using.
Si esta característica está deshabilitada, verás que la aplicación System ya no se compila ya que el espacio de nombres (en este ejemplo) ya no se declara.
PS C:Usersrichapp> type .app.csproj | findstr Implicit <ImplicitUsings>disable</ImplicitUsings> PS C:Usersrichapp> dotnet build Microsoft (R) Build Engine version 17.0.0-preview-21501-01+bbcce1dff for .NET Copyright (C) Microsoft Corporation. All rights reserved. Determining projects to restore... All projects are up-to-date for restore. You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview C:UsersrichappProgram.cs(2,1): error CS0103: The name 'Console' does not exist in the current context [C:Usersrichappapp.csproj] Build FAILED.
Otra opción es utilizar la función de uso global, que le permite usar solo los espacios de nombres que desee, como puede ver en el siguiente ejemplo.
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net6.0</TargetFramework> <ImplicitUsings>disable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <Using Include="System" /> </ItemGroup> </Project>
Ahora, construyendo el código.
PS C:Usersrichapp> type .app.csproj | findstr Using <ImplicitUsings>disable</ImplicitUsings> <Using Include="System" /> PS C:Usersrichapp> dotnet build Microsoft (R) Build Engine version 17.0.0-preview-21501-01+bbcce1dff for .NET Copyright (C) Microsoft Corporation. All rights reserved. Determining projects to restore... All projects are up-to-date for restore. You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview app -> C:UsersrichappbinDebugnet6.0app.dll Build succeeded. 0 Warning(s) 0 Error(s) Time Elapsed 00:00:00.80
Ese no es el patrón general recomendado, pero es una buena opción para las personas que desean el máximo control. En la mayoría de los casos, esperamos que los desarrolladores confíen en los usos implícitos proporcionados por el SDK y aprovechen los usos globales explícitos para espacios de nombres de su propio código o paquetes NuGet que usan de manera generalizada.
Nullable
Se ha actualizado Program.cs para demostrar los tipos de referencia anulables. La aplicación llama a un método que devuelve un T?, en este caso una cadena anulable (string?).
List<string> greetings = new() { "Nice day, eh?" }; string? hello = greetings.Find(x => x.EndsWith("!")); string greeting = hello ?? "Hello world!"; Console.WriteLine($"There are {greeting.Length} characters in "{greeting}"");
La línea que define hello la variable no se computará sin definirse como var, string? , o abordar el caso en el que List<T>. Encontrar devuelve null. Sin la función anulable habilitada, podría pasar por alto este problema, lo que provocaría que mi código se bloquea con una excepción NullReferenceException. Eso no es bueno. Estoy manejando eso en la siguiente línea con , el operador de coalescencia nulo. En la mayoría de los casos, estas dos líneas se fusionarán en una sola, como se puede ver en el siguiente código. Los he mantenido separados (en este ejemplo artificial) para que pueda ver mi uso de para acomodar una API que devuelve un tipo de referencia nulable.
string greeting = greetings.Find(x => x.EndsWith(«!»)) ?? «Hello world!»;
En este ejemplo, se ve un fusionado de todo en una línea. Ahora poemos declarar la variable como string ya que la nulidad se ha acomodado con la cadena ?? que sigue . El string? en este caso es algo que solo el compilador ve.
string[] args
El parámetro permanece disponible con instrucciones de nivel superior. Es menos obvio. Aquí hay otro programa que demuestra su uso args.
string greeting = args.Length > 0 ? string.Join(" ", args) : "Hello World!"; WriteLine($"There are {greeting.Length} characters in "{greeting}" in this {nameof(Program)}.");
Produce el siguiente resultado.
PS C:\Users\rich\app> dotnet run There are 12 characters in "Hello World!" in this Program. PS C:\Users\rich\app> dotnet run Nice day, eh? There are 13 characters in "Nice day, eh?" in this Program.
Esta aplicación también demuestra que el tipo todavía está definido. Programa. Principal no lo es.
Ser eliminó Console agregando un uso estático global al archivo de proyecto, de la siguiente manera.
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net6.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <Using Include="System.Console" Static="True"/> </ItemGroup> </Project>
Definición de métodos regulares
Se han escuchado los comentarios de algunas personas de que los métodos reales son preferidos sobre las funciones locales, específicamente para la clase Program. Puede utilizar cualquier modelo con instrucciones de nivel superior.
Programa.cs.
WriteLine(GetTheGreeting(args));
Program.Foo.cs.
public partial class Program { public static string GetTheGreeting(string[] args) { string greeting = args.Length > 0 ? string.Join(" ", args) : "Hello World!"; return $"There are {greeting.Length} characters in "{greeting}" in this {nameof(Program)}."; } }
Produce el mismo resultado que el ejemplo anterior. El nombre de archivo es arbitrario. De hecho, también lo es . Puede nombrar estos archivos como desee. El compilador lo resolverá.Program.Foo.csProgram.cs
Como se mencionó anteriormente, el tipo Program debe estar en el espacio de nombres de nivel superior cuando se usan instrucciones de nivel superior.
Actualización de macOS y Windows Arm64
El proyecto para soportar macOS y Windows arm64 está casi terminado. Al principio, pensamos que el proyecto se trataba únicamente de admitir Arm64 en macOS y que Rosetta 2 cubriría x64.
Aqui un resumen rápido de dónde esta este punto para las máquinas macOS y Windows Arm64:
- .NET 6 RC2 permite la coexistencia de Arm64 + x64 mediante la instalación de compilaciones arm64 y x64 en diferentes ubicaciones. Hasta ahora, las construcciones Arm64 y x64 se sobrescriben entre sí, lo que llevó a la tristeza general.
- Debe desinstalar todas las compilaciones de .NET y comenzar desde cero (en máquinas macOS y Windows Arm64) para adoptar .NET 6 RC2+. Esto incluye Arm64 y x64, .NET 6 y pre-.NET 6.
- Las compilaciones anteriores a .NET 6 aún no están listas para instalarse.
- La CLI le permite utilizar el SDK de Arm64 para realizar el desarrollo de Arm64 Y x64 (suponiendo que tenga instalados los tiempos de ejecución de Arm64 y x64 necesarios). Lo mismo es cierto a la inversa.
- Queremos que la gente solo use el SDK de Arm64, ya que será una mejor experiencia (rendimiento de la arquitectura nativa; un SDK para mantener). Continuaremos mejorando el producto para que este modelo sea la opción fácil para la mayoría de los desarrolladores.
- Para el SDK, solo admitiremos .NET 6+ en Arm64. Las compilaciones anteriores del SDK se bloquearán en Arm64.
- Para los tiempos de ejecución, admitiremos todas las versiones compatibles, Arm64 y x64.
- .NET 6 RC2 ofrece la mayor parte de la experiencia final de .NET 6 para Arm64 (incluida la emulación x64).
- Esperamos actualizar los tiempos de ejecución de .NET Core 3.1 y .NET 5 para alinearlos con .NET 6 RTM (tanto a tiempo como con los requisitos técnicos). Eso todavía está por determinar.
- Las construcciones nocturnas de RC2 están actualmente rotas, por lo que tendrá que esperar un par de semanas más hasta que realmente enviemos RC2 para probar todo esto.
- El SDK de .NET 5 para Windows Arm64 dejará de ser compatible con las primeras condiciones, con .NET 6 RTM.
También estamos considerando hacer cambios radicales para unificar los argumentos que utilizamos para la segmentación de arquitectura:dotnet test
- [Rompiendo el cambio] Para la prueba dotnet, cambie -a alias -arch en lugar de –test-adapter-path
- [Rompiendo el cambio] Para la prueba dotnet, cambie -r a alias –runtime en lugar de –results-dir
Una gran parte del proyecto es permitir el uso de tiempos de ejecución x64 con el SDK de Arm64. Puedes ver eso demostrado en la siguiente imagen.
Si quieres más detalles, echa un vistazo a dotnet/sdk #21686.
Conclusión
C# 10 ofrece mejoras significativas en simplicidad y expresividad, basándose en características similares en C# 9. Parte de la motivación para estos cambios ha sido hacer que C# sea más atractivo para los nuevos programadores. y ahora a esperar la versión final!