Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este documento se enumeran los cambios importantes conocidos en Roslyn desde la versión general de .NET 10 (.NET SDK versión 10.0.100) hasta la versión general de .NET 11 (.NET SDK versión 11.0.100).
El contexto seguro de una expresión de colección de tipo Span/ReadOnlySpan es ahora bloque de declaración.
Introducido en Visual Studio 2026, versión 18.3
El compilador de C# realizó un cambio importante para cumplir correctamente las reglas de seguridad ref en la especificación de características de expresiones de colección . En concreto, la cláusula siguiente:
- Si el tipo de destino es un tipo de intervalo
System.Span<T>oSystem.ReadOnlySpan<T>, el contexto seguro de la expresión de colección es el bloque de declaración.
Anteriormente, el compilador usaba un miembro de función de contexto seguro en esta situación. Ahora hemos realizado un cambio para usar el bloque de declaración según la especificación. Esto puede hacer que aparezcan nuevos errores en el código existente, como en el escenario siguiente:
scoped Span<int> items1 = default;
scoped Span<int> items2 = default;
foreach (var x in new[] { 1, 2 })
{
Span<int> items = [x];
if (x == 1)
items1 = items; // previously allowed, now an error
if (x == 2)
items2 = items; // previously allowed, now an error
}
Si el código se ve afectado por este cambio importante, considere la posibilidad de usar un tipo de matriz para las expresiones de colección pertinentes en su lugar:
scoped Span<int> items1 = default;
scoped Span<int> items2 = default;
foreach (var x in new[] { 1, 2 })
{
int[] items = [x];
if (x == 1)
items1 = items; // ok, using 'int[]' conversion to 'Span<int>'
if (x == 2)
items2 = items; // ok
}
Como alternativa, mueva la expresión de recopilación a un ámbito donde se permita la asignación.
scoped Span<int> items1 = default;
scoped Span<int> items2 = default;
Span<int> items = [0];
foreach (var x in new[] { 1, 2 })
{
items[0] = x;
if (x == 1)
items1 = items; // ok
if (x == 2)
items2 = items; // ok
}
Consulte también https://github.com/dotnet/csharplang/issues/9750.
Los escenarios que requieren que el compilador sintetice un delegado que devuelve ref readonly ahora requieren la disponibilidad del tipo System.Runtime.InteropServices.InAttribute.
Introducido en Visual Studio 2026, versión 18.3
El compilador de C# realizó un cambio importante para emitir correctamente metadatos para ref readonly devolver delegados sintetizados
Esto puede provocar un "error CS0518: El tipo predefinido "System.Runtime.InteropServices.InAttribute" no está definido o importado" para que aparezca en el código existente, como en los escenarios siguientes:
var d = this.MethodWithRefReadonlyReturn;
var d = ref readonly int () => ref x;
Si el código se ve afectado por este cambio importante, considere la posibilidad de agregar una referencia a un ensamblado que define System.Runtime.InteropServices.InAttribute al proyecto.
Los escenarios que utilizan las funciones locales de ref readonly ahora requieren la disponibilidad del tipo System.Runtime.InteropServices.InAttribute.
Introducido en Visual Studio 2026, versión 18.3
El compilador de C# realizó un cambio importante para emitir correctamente metadatos para ref readonly devolver funciones locales.
Esto puede provocar un "error CS0518: El tipo predefinido "System.Runtime.InteropServices.InAttribute" no está definido o importado" para que aparezca en el código existente, como en el escenario siguiente:
void Method()
{
...
ref readonly int local() => ref x;
...
}
Si el código se ve afectado por este cambio importante, considere la posibilidad de agregar una referencia a un ensamblado que define System.Runtime.InteropServices.InAttribute al proyecto.
No se permite la evaluación dinámica de &&/|| operadores con el operando izquierdo escrito estáticamente como una interfaz.
Introducido en Visual Studio 2026, versión 18.3
El compilador de C# ahora notifica un error cuando se usa un tipo de interfaz como operando izquierdo de un operador lógico && o || con un dynamic operando derecho.
Anteriormente, el código se compilaba para un tipo de interfaz con true/false operadores, pero fallaba en tiempo de ejecución con un RuntimeBinderException, porque el enlazador en tiempo de ejecución no puede invocar operadores definidos en interfaces.
Este cambio evita un error en tiempo de ejecución al notificarlo en tiempo de compilación en su lugar. El mensaje de error es:
error CS7083: La expresión debe convertirse implícitamente en booleano o su tipo "I1" no debe ser una interfaz y debe definir el operador "false".
interface I1
{
static bool operator true(I1 x) => false;
static bool operator false(I1 x) => false;
}
class C1 : I1
{
public static C1 operator &(C1 x, C1 y) => x;
public static bool operator true(C1 x) => false;
public static bool operator false(C1 x) => false;
}
void M()
{
I1 x = new C1();
dynamic y = new C1();
_ = x && y; // error CS7083: Expression must be implicitly convertible to Boolean or its type 'I1' must not be an interface and must define operator 'false'.
}
Si el código se ve afectado por este cambio disruptivo, considere cambiar el tipo estático del operando izquierdo de un tipo de interfaz a un tipo de clase concreto, o al tipo dynamic.
void M()
{
I1 x = new C1();
dynamic y = new C1();
_ = (C1)x && y; // Valid - uses operators defined on C1
_ = (dynamic)x && y; // Valid - uses operators defined on C1
}
Consulte también https://github.com/dotnet/roslyn/issues/80954.
nameof(this.) está prohibido en los atributos
Introducido en Visual Studio 2026 versión 18.3 y .NET 10.0.200
El uso de las palabras clave this o base dentro de nameof en un atributo se permitió involuntariamente en Roslyn desde C# 12 y ahora está correctamente prohibido para coincidir con la especificación del lenguaje.
Este cambio importante se puede mitigar quitando this. y accediendo al miembro sin utilizar el calificador.
Consulte también https://github.com/dotnet/roslyn/issues/82251.
class C
{
string P;
[System.Obsolete(nameof(this.P))] // now disallowed
[System.Obsolete(nameof(P))] // workaround
void M() { }
}
Análisis de "with" dentro de un brazo de expresión de switch
Introducido en Visual Studio 2026 versión 18.4
Ver https://github.com/dotnet/roslyn/issues/81837 y https://github.com/dotnet/roslyn/pull/81863
Anteriormente, al ver lo siguiente, el compilador trataría (X.Y)when como una expresión de conversión. En concreto, convierta el identificador when contextual en (X.Y):
x switch
{
(X.Y) when
}
Esto no era deseable y significaba que una comprobación simple when del patrón (como (X.Y) when a > b =>) no se analizaría correctamente. Ahora, esto se trata como un patrón (X.Y) constante seguido de when clause.
with()como un elemento de expresión de colección se trata como argumentos de construcción de colección
Introducido en Visual Studio 2026 versión 18.4
with(...) cuando se usa como elemento en una expresión de colección y cuando LangVersion se establece en 15 o superior, se enlaza como argumentos pasados al constructor o al método de fábrica usado para crear la colección, en lugar de como una expresión de invocación de un método denominado with.
Para enlazar a un método denominado with, use @with en su lugar.
object x, y, z = ...;
object[] items;
items = [with(x, y), z]; // C# 14: call to with() method; C# 15: error args not supported for object[]
items = [@with(x, y), z]; // call to with() method
object with(object a, object b) { ... }
Los tipos de puntero ya no requieren un contexto no seguro
Introducido en Visual Studio 2026 versión 18.7
En una versión futura de C# (actualmente en langversion:preview), los tipos de puntero (por ejemplo, int*, delegate*<void>) ya no requieren un contexto no seguro.
Solo las operaciones de direccionamiento indirecto de puntero (desreferencia, acceso de miembros a través de ->, acceso a elementos, etc.) requieren inseguridad.
Esto forma parte de la característica de evolución no segura .
Dado que los tipos de puntero ahora son válidos en contextos seguros, la resolución de sobrecarga puede considerar candidatos que antes estaban excluidos. Esto puede provocar nuevos errores de ambigüedad:
using System;
class Program
{
static void Main()
{
M(x => { }); // C# 14: prints "2"; C# preview: error CS0121 (ambiguous)
}
static void M(F1 f) { Console.WriteLine(1); }
static void M(F2 f) { Console.WriteLine(2); }
}
unsafe delegate void F1(int* x);
delegate void F2(int x);
Anteriormente, la expresión lambda x => { } no podía convertir a F1 en un contexto seguro porque int* requería un contexto no seguro, por lo que solo M(F2) era aplicable.
Ahora que int* es válido en contextos seguros, la expresión lambda se puede convertir en ambos delegados, lo que produce un error de ambigüedad.
Si el código se ve afectado por el cambio de ambigüedad, agregue tipos de parámetros explícitos a la lambda para desambiguar:
M((int x) => { }); // Resolves to M(F2)
stackalloc no inicializado dentro de SkipLocalsInit requiere un contexto no seguro
Introducido en Visual Studio 2026 versión 18.7
En una versión futura de C# (actualmente en langversion:preview), una stackalloc expresión sin inicializador dentro de un método marcado con [SkipLocalsInit] ahora requiere un contexto no seguro, incluso cuando el tipo de destino es Span<T>.
Esto se debe a que SkipLocalsInit evita la inicialización cero de la memoria asignada, lo que permite leer datos sin inicializar: un problema de seguridad de memoria.
Esto forma parte de la característica de evolución no segura .
[System.Runtime.CompilerServices.SkipLocalsInit]
void M()
{
Span<int> a = stackalloc int[5]; // previously ok, now error CS9361
Span<int> b = stackalloc int[] { 1, 2 }; // ok (has initializer)
Span<int> c = stackalloc int[2] { 1, 2 }; // ok (has initializer)
}
Si el código se ve afectado, puede hacer lo siguiente:
- Agregue un
unsafebloque alrededor destackalloc:[SkipLocalsInit] void M() { Span<int> a; unsafe { a = stackalloc int[5]; } } - O bien, proporcione un inicializador para que la memoria se inicialice completamente:
[SkipLocalsInit] void M() { Span<int> a = stackalloc int[5] { 0, 0, 0, 0, 0 }; }
Roslyn breaking changes