Microsoft lanzó recientemente C# 10 y .Net 6 al mundo, pero que novedades trae esta versión, echemos una revisada a lo nuevo en C#.
En el modo vista es donde podemos encontrar más características disponibles. C# 10 es compatible con .NET 6.
Structs de registro
Podemos declarar los registros de tipo de valor mediante las declaraciones o readonly record struct. Se puede aclarar que un elemento record es un tipo de referencia con la declaración record class.
Mejoras de tipos de estructura
C# 10 presenta las mejoras siguientes relacionadas con los tipos de estructura:
Se puede declarar un constructor sin parámetros de instancia en un tipo de estructura e inicializar un campo o propiedad de instancia en su declaración.
Un operando izquierdo de la expresión puede ser de cualquier tipo de estructura o un tipo anónimo (de referencia).
Controlador de cadena interpolada
Se puede crear un tipo que compile la cadena resultante a partir de una expresión de cadena interpolada. Las bibliotecas de .NET usan esta característica en muchas API.
Directivas using globales
Se puede agregar el modificador a global cualquier global para indicar al compilador que la directiva se aplica a todos los archivos de código fuente de la compilación. Normalmente, se trata de todos los archivos de código fuente de un proyecto.
Declaración de espacios de nombres con ámbito de archivo
Se puede usar una nueva forma de la declaración para declarar que todas las declaraciones posteriores son miembros del espacio de nombres declarado:
namespace MyNamespace;
Esta nueva sintaxis ahorra espacio horizontal y vertical para las declaraciones namespace.
Patrones de propiedades extendidos
A partir de C# 10, se puede hacer referencia a propiedades o campos anidados en un patrón de propiedad. Por ejemplo, un patrón con el formato
{ Prop1.Prop2: pattern }
es válido en C# 10 y versiones posteriores, y equivalente a
{ Prop1: { Prop2: pattern } }
válido en C# 8.0 y versiones posteriores.
Mejoras de expresiones lambda
C# 10 incluye muchas mejoras en el modo en que se controlan las expresiones lambda:
- Las expresiones lambda pueden tener un tipo natural, donde el compilador puede deducir un tipo delegado de la expresión lambda o del grupo de métodos.
- Las expresiones lambda pueden declarar un tipo de valor devuelto cuando el compilador no puede deducirlo.
- Los atributos se pueden aplicar a las expresiones lambda.
Estas características hacen que las expresiones lambda sean parecidas a los métodos y las funciones locales. Facilitan el uso de expresiones lambda sin declarar una variable de un tipo delegado y funcionan de forma más fluida con las nuevas API mínimas de ASP.NET CORE.
Cadenas interpoladas constantes
En C# 10, las cadenas const se pueden inicializar mediante cadenas const si todos los marcadores de posición son cadenas constantes. La interpolación de cadenas puede crear cadenas constantes más legibles a medida que se compilan las cadenas constantes usadas en la aplicación. Las expresiones de marcador de posición no pueden ser constantes numéricas porque esas constantes se convierten en cadenas en tiempo de ejecución. La referencia cultural actual puede afectar a su representación de cadena.
Los tipos de registro pueden sellar ToString
En C# 10, se puede agregar el modificador sealed al invalidar ToString en un tipo de registro. Al sellar el método ToString, se impide que el compilador sintetice un método ToString para cualquier tipo de registro derivado. Garantiza sealedToString que todos los tipos de registro derivados usan el método ToString definido en un tipo de registro base común.
Asignación y declaración en la misma desconstrucción
Este cambio quita una restricción de versiones anteriores de C#. Anteriormente, una desconstrucción podía asignar todos los valores a variables existentes, o bien inicializar variables recién declaradas:
// Initialization:
(int x, int y) = point;
// assignment:
int x1 = 0;
int y1 = 0;
(x1, y1) = point;
En C# 10 se elimina esta restricción:
int x = 0;
(x, int y) = point;
Asignación definitiva mejorada
Antes de C# 10, había muchos escenarios en los que la asignación definitiva y el análisis de estado NULL generaban advertencias que eran falsos positivos. Por lo general, estas implicaban comparaciones con constantes booleanas, el acceso a una variable solo en las instrucciones true o false de una instrucción if, y expresiones de uso combinado de NULL. Estos ejemplos generaron advertencias en versiones anteriores de C#, pero no en C# 10:
string representation = "N/A";
if ((c != null && c.GetDependentValue(out object obj)) == true)
{
representation = obj.ToString(); // undesired error
}
// Or, using ?.
if (c?.GetDependentValue(out object obj) == true)
{
representation = obj.ToString(); // undesired error
}
// Or, using ??
if (c?.GetDependentValue(out object obj) ?? false)
{
representation = obj.ToString(); // undesired error
}
El impacto principal de esta mejora es que las advertencias para la asignación definitiva y el análisis de estado NULL son más precisas.
Se permite el atributo AsyncMethodBuilder en los métodos
En C# 10 y versiones posteriores, puede especificar un generador de métodos asincrónicos diferente para un único método, además de especificar el tipo de generador de métodos para todos los métodos que devuelven un tipo concreto similar a una tarea. Un generador de métodos asíncronos personalizado permite escenarios avanzados de optimización del rendimiento en los que un método determinado puede beneficiarse de un generador personalizado.
Diagnóstico del atributo CallerArgumentExpression
Puede usar System.Runtime.CompilerServices.CallerArgumentExpressionAttribute para especificar un parámetro que el compilador reemplace por la representación de texto de otro argumento. Esta característica permite a las bibliotecas crear diagnósticos más específicos. El código siguiente prueba una condición. Si la condición es false, el mensaje de excepción incluye la representación de texto del argumento pasado a condition:
public static void Validate(bool condition, [CallerArgumentExpression("condition")] string? message=null)
{
if (!condition)
{
throw new InvalidOperationException($"Argument failed validation: <{message}>");
}
}
Pragma #line mejorado
C# 10 admite un formato nuevo para el pragma #line. Es probable que no use el formato nuevo, pero verá sus efectos. Las mejoras permiten una salida más detallada en lenguajes específicos de dominio (DSL), como Razor. El motor de Razor usa estas mejoras para mejorar la experiencia de depuración. Encontrará que los depuradores pueden resaltar el origen de Razor con más precisión. Para obtener más información sobre la nueva sintaxis.
Atributos genéricos
Importante
Los atributos genéricos son una característica en vista previa. Debe establecer en Preview para habilitar esta característica. Esta característica puede cambiar antes de su versión final.
Puede declarar una clase genérica cuya clase base es . Esto proporciona una sintaxis más cómoda de los atributos que requieren un parámetro System.Type. Antes había que crear un atributo que tomara Type como parámetro de constructor:
public class TypeAttribute : Attribute
{
public TypeAttribute(Type t) => ParamType = t;
public Type ParamType { get; }
}
Y, para aplicar el atributo, se usa el operador typeof:
[TypeAttribute(typeof(string))]
public string Method() => default;
Con esta nueva característica, puede crear un atributo genérico en su lugar:
public class GenericAttribute : Attribute { }
Luego, especifique el parámetro de tipo para usar el atributo:
[GenericAttribute()]
public string Method() => default;
Puede aplicar un atributo genérico construido completamente cerrado; en otras palabras, se deben especificar todos los parámetros de tipo. Por ejemplo, la siguiente vista no se admite:
public class GenericType
{
[GenericAttribute()] // Not allowed! generic attributes must be fully closed types.
public string Method() => default;
}
Los argumentos de tipo deben cumplir las mismas restricciones que el operador typeof. No se permiten los tipos que requieren anotaciones de metadatos. Entre otros, se incluyen los siguientes ejemplos:
- dynamic
- nint, nuint
- string? (o cualquier tipo de referencia que acepte valores NULL)
- (int X, int Y) (o cualquier otro tipo de tupla que use la sintaxis de tupla de C#)
Estos tipos no se representan directamente en los metadatos, incluyen anotaciones que describen el tipo. En todos los casos, se puede usar el tipo subyacente en su lugar:
- object para dynamic
- IntPtr en lugar de nint o unint
- string en lugar de string?.
- ValueTuple en lugar de (int X, int Y).
Miembros abstractos estáticos en interfaces
Importante
Los miembros abstractos estáticos de las interfaces son una característica en versión preliminar. Debe agregar en el True el archivo del proyecto. Puedes experimentar con esta característica y las bibliotecas experimentales que la usan. Usaremos los comentarios de los ciclos de versión preliminar para mejorar la característica antes de su versión general.
Puede agregar miembros abstractos estáticos en interfaces para definir interfaces que incluyan operadores sobrecargables, otros miembros estáticos y propiedades estáticas. El escenario principal de esta característica es usar operadores matemáticos en tipos genéricos. El equipo del entorno de ejecución de .NET ha incluido interfaces para operaciones matemáticas en el paquete de NuGet System.Runtime.Experimental. Por ejemplo, puede implementar en System.IAdditionOperators un tipo que implemente operator +. Otras interfaces definen otras operaciones matemáticas o valores bien definidos.
Puede obtener más información y probar la característica usted mismo en el tutorial Explore static abstract interface members (Exploración de miembros de interfaz abstracta estática) o en la entrada de blog Preview features in .NET 6 – generic math (Características en versión preliminar de .NET 6: matemáticas genéricas).