Compartir vía


Novedades de C# 14

C# 14 incluye las siguientes características nuevas. Puede probar estas características con la versión más reciente de Visual Studio 2026 o el SDK de .NET 10:

C# 14 es la versión más reciente de C#. C# 14 se admite en .NET 10. Para obtener más información, vea Control de versiones del lenguaje C#.

Puede descargar el SDK de .NET 10 más reciente desde la página de descargas de .NET. También puede descargar Visual Studio 2026, que incluye el SDK de .NET 10.

Puede encontrar los cambios importantes introducidos en C# 14 en nuestro artículo sobre los cambios importantes.

Nota:

Estamos interesados en sus comentarios sobre estas características. Si encuentra problemas con cualquiera de estas nuevas características, cree un nuevo problema en el repositorio dotnet/roslyn.

Miembros de extensión

C# 14 agrega una nueva sintaxis para definir miembros de extensión. La nueva sintaxis permite declarar propiedades de extensión además de métodos de extensión. También puede declarar miembros de extensión que amplían el tipo, en lugar de una instancia del tipo. En otras palabras, estos nuevos miembros de extensión pueden aparecer como miembros estáticos en el tipo que extiendes. Estas extensiones pueden incluir operadores definidos por el usuario implementados como métodos de extensión estáticos. En el ejemplo de código siguiente se muestra un ejemplo de los diferentes tipos de miembros de extensión que puede declarar:

public static class Enumerable
{
    // Extension block
    extension<TSource>(IEnumerable<TSource> source) // extension members for IEnumerable<TSource>
    {
        // Extension property:
        public bool IsEmpty => !source.Any();

        // Extension method:
        public IEnumerable<TSource> Where(Func<TSource, bool> predicate) { ... }
    }

    // extension block, with a receiver type only
    extension<TSource>(IEnumerable<TSource>) // static extension members for IEnumerable<Source>
    {
        // static extension method:
        public static IEnumerable<TSource> Combine(IEnumerable<TSource> first, IEnumerable<TSource> second) { ... }

        // static extension property:
        public static IEnumerable<TSource> Identity => Enumerable.Empty<TSource>();

        // static user defined operator:
        public static IEnumerable<TSource> operator + (IEnumerable<TSource> left, IEnumerable<TSource> right) => left.Concat(right);
    }
}

Los miembros del primer bloque de extensión se llaman como si fueran miembros de instancia de IEnumerable<TSource>, por ejemplo sequence.IsEmpty. Los miembros del segundo bloque de extensión se llaman como si fueran miembros estáticos de IEnumerable<TSource>, por ejemplo IEnumerable<int>.Identity.

Para obtener más información, lea el artículo sobre los miembros de la extensión en la guía de programación, el artículo de referencia de lenguaje sobre la extension palabra clave y la especificación de características para la nueva característica de miembros de extensión.

La palabra clave field.

El token field permite escribir un cuerpo de descriptor de acceso de propiedad sin declarar un campo de respaldo explícito. El token field se reemplaza por un campo de respaldo sintetizado del compilador.

Por ejemplo, anteriormente, si querías asegurarte de que una propiedad string no se podía establecer a null, tenías que declarar un campo de respaldo e implementar ambos accesadores.

private string _msg;
public string Message
{
    get => _msg;
    set => _msg = value ?? throw new ArgumentNullException(nameof(value));
}

Ahora puede simplificar el código para:

public string Message
{
    get;
    set => field = value ?? throw new ArgumentNullException(nameof(value));
}

Puede declarar un cuerpo para uno o ambos descriptores de acceso en una propiedad auxiliar de campo.

Es posible que hay cambio importante o se produzca cierta confusión al leer el código en tipos que también incluyen un símbolo llamado field. Puede usar @field o this.field para desambiguar entre la field palabra clave y el identificador, o bien puede cambiar el nombre del símbolo actual field para proporcionar una mejor distinción.

Conversiones de rango implícitas

C# 14 presenta compatibilidad de primera clase con System.Span<T> y System.ReadOnlySpan<T> en el lenguaje . Esta compatibilidad implica nuevas conversiones implícitas que permiten una programación más natural con estos tipos.

Span<T> y ReadOnlySpan<T> se usan de muchas maneras clave en C# y en tiempo de ejecución. Su introducción mejora el rendimiento sin riesgo de seguridad. C# 14 reconoce la relación y admite algunas conversiones entre ReadOnlySpan<T>, Span<T>y T[]. Los tipos de intervalo pueden ser receptores de métodos de extensión, componerse con otras conversiones y facilitar la inferencia de tipos genéricos.

Puede encontrar la lista de conversiones implícitas de span en el artículo sobre los tipos integrados en la sección de referencia del lenguaje. Puede obtener más información leyendo la especificación de características para los tipos de intervalo de primera clase.

Tipos genéricos sin enlazar y nameof

A partir de C# 14, el argumento para nameof puede ser un tipo genérico sin enlazar. Por ejemplo, nameof(List<>) se evalúa como List. En versiones anteriores de C#, solo se podían usar tipos genéricos cerrados, como List<int>, para devolver el List nombre.

Parámetros lambda simples con modificadores

Puede agregar modificadores de parámetro, como scoped, ref, in, outo ref readonly a parámetros de expresión lambda sin especificar el tipo de parámetro:

delegate bool TryParse<T>(string text, out T result);
// ...
TryParse<int> parse1 = (text, out result) => Int32.TryParse(text, out result);

Anteriormente, solo se permitía agregar los modificadores cuando las declaraciones de parámetros incluían los tipos de los parámetros. La declaración anterior requeriría tipos en todos los parámetros:

TryParse<int> parse2 = (string text, out int result) => Int32.TryParse(text, out result);

El params modificador todavía requiere una lista de parámetros con tipo explícito.

Puede obtener más información sobre estos cambios en el artículo sobre expresiones lambda en la referencia del lenguaje C#.

Más miembros parciales

Ahora puede declarar constructores de instancia y eventos como miembros parciales.

Los constructores parciales y los eventos parciales deben incluir exactamente una declaración de definición y una declaración de implementación.

Solo la declaración de implementación de un constructor parcial puede incluir un inicializador de constructor: this() o base(). Solo una declaración de tipo parcial puede incluir la sintaxis del constructor principal.

La declaración de implementación de un evento parcial debe incluir descriptores de acceso add y remove. La declaración de definición declara un evento similar a un campo.

Asignación compuesta definida por el usuario

Puede obtener más información en la especificación de características para la asignación compuesta definida por el usuario.

Asignación condicional null

Los operadores de acceso a miembros condicionales nulos, ?. y ?[], ahora se pueden utilizar en el lado izquierdo de una asignación o asignación compuesta.

Antes de C# 14, era necesario comprobar un valor NULL de una variable antes de asignar a una propiedad:

if (customer is not null)
{
    customer.Order = GetCurrentOrder();
}

Puede simplificar el código anterior mediante el ?. operador :

customer?.Order = GetCurrentOrder();

El lado derecho del = operador solo se evalúa cuando el lado izquierdo no es NULL. Si customer es null, el código no llama a GetCurrentOrder.

Además de la asignación, puede usar operadores de acceso a miembros condicionales NULL con operadores de asignación compuestos (+=, -=, y otros). Sin embargo, no se permiten los incrementos ++ y los decrementos --.

Puede obtener más información en el artículo de referencia del lenguaje sobre el acceso condicional a miembros y la especificación de características para la asignación condicional nula.

Consulte también