Nota
O acceso a esta páxina require autorización. Pode tentar iniciar sesión ou modificar os directorios.
O acceso a esta páxina require autorización. Pode tentar modificar os directorios.
A lo largo de su historial, .NET ha intentado mantener un alto nivel de compatibilidad de la versión a la versión y entre implementaciones de .NET. Aunque .NET 5 (y .NET Core) y versiones posteriores se pueden considerar como una nueva tecnología en comparación con .NET Framework, dos factores principales limitan la capacidad de esta implementación de .NET para diferir de .NET Framework:
- Un gran número de desarrolladores originalment han desarrollado o siguen desarrollando aplicaciones de .NET Framework. Esperan un comportamiento coherente en las implementaciones de .NET.
- .NET proyectos de biblioteca estándar permiten a los desarrolladores crear bibliotecas destinadas a API comunes compartidas por .NET Framework y .NET 5 (y .NET Core) y versiones posteriores. Los desarrolladores esperan que una biblioteca usada en una aplicación de .NET se comporte de forma idéntica a la misma biblioteca que se usa en una aplicación de .NET Framework.
Junto con la compatibilidad entre implementaciones de .NET, los desarrolladores esperan un alto nivel de compatibilidad entre versiones de una implementación determinada de .NET. En concreto, el código escrito para una versión anterior de .NET Core debe ejecutarse sin problemas en .NET 5 o una versión posterior. De hecho, muchos desarrolladores esperan que las nuevas API que se encuentran en las versiones recién publicadas de .NET también sean compatibles con las versiones preliminares en las que se introdujeron esas API.
En este artículo se describen los cambios que afectan a la compatibilidad y a la forma en que el equipo de .NET evalúa cada tipo de cambio. Comprender cómo el equipo de .NET aborda los posibles cambios importantes es especialmente útil para los desarrolladores que abren pull requests que modifican el comportamiento de las API existentes en .NET.
En las secciones siguientes se describen las categorías de cambios realizados en .NET API y su impacto en la compatibilidad de aplicaciones. Los cambios se permiten (✔️), no se permiten (❌) o requieren juicio y una evaluación de cómo de predecible, obvio y coherente fue el comportamiento anterior (❓).
Nota:
- Además de servir como guía sobre cómo se evalúan los cambios en las bibliotecas de .NET, los desarrolladores de bibliotecas también pueden usar estos criterios para evaluar los cambios en sus bibliotecas destinadas a varias implementaciones y versiones de .NET.
- Para obtener información sobre las categorías de compatibilidad, por ejemplo, compatibilidad con versiones anteriores y posteriores, vea Cómo los cambios de código pueden afectar a la compatibilidad.
Modificaciones en el contrato público
Los cambios en esta categoría modifican el área expuesta pública de un tipo. La mayoría de los cambios de esta categoría no se permiten, ya que infringen la compatibilidad con versiones anteriores (la capacidad de una aplicación desarrollada con una versión anterior de una API para ejecutarse sin volver a compilarla en una versión posterior).
Tipos
✔️ PERMITIDO: quitar una implementación de interfaz de un tipo cuando una interfaz ya está implementada por un tipo base
❓ REQUIERE CRITERIO: Agregar una nueva implementación de interfaz a un tipo
Se trata de un cambio aceptable porque no afecta negativamente a los clientes existentes. Los cambios en el tipo deben funcionar dentro de los límites de los cambios aceptables definidos aquí para que la nueva implementación siga siendo aceptable. Es necesario tener precaución extrema al agregar interfaces que afectan directamente a la capacidad de un diseñador o serializador para generar código o datos que no se pueden consumir de nivel descendente. Un ejemplo es la ISerializable interfaz .
❓ REQUIERE SENTENCIA: Presentación de una nueva clase base
Un tipo se puede introducir en una jerarquía entre dos tipos existentes si no introduce nuevos miembros abstractos ni cambia la semántica o el comportamiento de los tipos existentes. Por ejemplo, en .NET Framework 2.0, la clase DbConnection se convirtió en una nueva clase base para SqlConnection, que anteriormente se había derivado directamente de Component.
✔️ PERMITIDO: Mover un tipo de un ensamblado a otro
El ensamblado anterior debe marcarse con el TypeForwardedToAttribute que apunta al nuevo ensamblado.
✔️ PERMITIDO: Cambiar un tipo struct a un tipo
readonly structNo se permite cambiar un
readonly structtipo a unstructtipo.✔️ ALLOWED: adición de la palabra clave sealed o abstract a un tipo cuando no hay constructores accesibles (públicos o protegidos)
✔️ PERMITIDO: expandir la visibilidad de un tipo
❌ NO PERMITIDO: Cambiar el espacio de nombres o el nombre de un tipo
❌ NO PERMITIDO: Cambio de nombre o eliminación de un tipo público
Esto interrumpe todo el código que utiliza el tipo cuyo nombre se ha cambiado o quitado.
Nota:
En raras ocasiones, .NET puede quitar una API pública. Para obtener más información, consulte eliminación de API en .NET. Para obtener información sobre la directiva de soporte técnico de .NET, consulte .NET Directiva de soporte técnico.
❌ NO PERMITIDO: Cambiar el tipo subyacente de una enumeración
Se trata de un cambio importante en tiempo de compilación y de comportamiento, así como de un cambio importante binario que puede hacer que los argumentos de atributos no se puedan analizar.
❌ NO PERMITIDO: Sellado de un tipo que anteriormente no estaba sellado
❌ NO PERMITIDO: Agregar una interfaz al conjunto de tipos base de una interfaz
Si una interfaz implementa una interfaz que previamente no implementaba, todos los tipos que implementaron la versión original de la interfaz se rompen.
❓ REQUIERE SENTENCIA: Quitar una clase del conjunto de clases base o una interfaz del conjunto de interfaces implementadas
Hay una excepción a la regla para la eliminación de la interfaz: puede agregar la implementación de una interfaz que deriva de la interfaz eliminada. Por ejemplo, puede eliminar IDisposable si el tipo o la interfaz ahora implementa IComponent, el cual implementa IDisposable.
❌ NO PERMITIDO: Cambio de un
readonly structtipo a un tipo de estructuraSin embargo, se permite el cambio de un
structtipo a unreadonly structtipo.❌ NO PERMITIDO: Cambio de un tipo de estructura a un
ref structtipo y viceversa❌ NO PERMITIDO: Reducción de la visibilidad de un tipo
Sin embargo, se permite aumentar la visibilidad de un tipo.
Miembros
✔️ PERMITIDO: Ampliar la visibilidad de un miembro que no es virtual
✔️ PERMITIDO: Adición de un miembro de tipo abstract a un tipo público que no tiene ningún constructor accesible (público o protegido) o el tipo sealed
Sin embargo, no se permite agregar un miembro abstracto a un tipo que tenga constructores accesibles (públicos o protegidos) y que no sea
sealed.✔️ ALLOWED: restricción de la visibilidad de un miembro protegido cuando el tipo no tiene constructores accesibles (públicos o protegidos) o el tipo está sellado.
✔️ PERMITIDO: Desplazamiento de un miembro a una clase superior en la jerarquía que el tipo del que fue eliminado
✔️ PERMITIDO: Adición o eliminación de una invalidación
La introducción de una invalidación puede hacer que los consumidores anteriores omitan la invalidación cuando llamen a la base.
✔️ ALLOWED: agregar un constructor a una clase, junto con un constructor sin parámetros si la clase anteriormente no tenía constructores.
Sin embargo, no se permite agregar un constructor a una clase que antes no tenía constructores sin agregar el constructor sin parámetros.
✔️ PERMITIDO: Cambio de un valor devuelto
ref readonlya unref(excepto para los métodos o interfaces virtuales)✔️ PERMITIDO: Quitar readonly de un campo, a menos que el tipo estático del campo sea un tipo de valor mutable.
✔️ PERMITIDO: Llamada a un nuevo evento que no se ha definido anteriormente
❓ REQUIERE SENTENCIA: Agregar un nuevo campo de instancia a un tipo
Este cambio afecta a la serialización.
❌ NO PERMITIDO: Cambiar el nombre o quitar un miembro o parámetro público
Esto afecta todo el código que utiliza el miembro renombrado o eliminado, o el parámetro.
Esto incluye el cambio de nombre o eliminación de un captador o establecedor de una propiedad, así como el cambio de nombre o eliminación de los miembros de la enumeración.
❓ REQUIERE CRITERIO: Agregar un miembro a una interfaz
Aunque es un cambio importante en el sentido de que eleva la versión mínima de .NET a .NET Core 3.0 (C# 8.0), que es cuando se introdujeron miembros de interfaz default (DIMs), se permite agregar un miembro estático, no abstracto y no virtual a una interfaz.
Si proporciona una implementación, agregar un nuevo miembro a una interfaz existente no producirá necesariamente errores de compilación en ensamblados posteriores. Sin embargo, no todos los lenguajes admiten DIMs. Además, en algunos escenarios, el entorno de ejecución no puede decidir qué miembro de interfaz predeterminado invocar. A partir de C# 13,
ref structlos tipos pueden implementar interfaces, pero no se pueden boxear ni convertir en un tipo de interfaz. Por lo tanto, unref structtipo debe proporcionar una implementación explícita para cada miembro de interfaz de instancia; no puede usar la implementación predeterminada proporcionada por la interfaz. Agregar un miembro de instancia predeterminado a una interfaz que es implementada porref structrequiere queref structañada una implementación correspondiente, lo que constituye un cambio importante en el origen. Por estos motivos, use el criterio al agregar un miembro a una interfaz existente.Nota:
Si la interfaz se implementa mediante tipos
ref struct(posible en C# 13 y versiones posteriores), agregar cualquier miembro de instancia predeterminado a la interfaz es un cambio de ruptura para esos llamadores.ref structdebe proporcionar una implementación explícita del nuevo miembro; no se puede revertir a la implementación predeterminada.❌ NO PERMITIDO: Cambiar el valor de una constante pública o un miembro de enumeración
❌ NO PERMITIDO: Cambiar el tipo de una propiedad, un campo, un parámetro o un valor devuelto
❌ NO PERMITIDO: Agregar, quitar o cambiar el orden de los parámetros
❌ NO PERMITIDO: agregar o quitar la palabra clave in, out o ref de un parámetro
✔️ ALLOWED: cambio de un
refparámetro aref readonlyCambiar un parámetro de
refaref readonlyes compatible con el origen para los sitios de llamadas existentes que pasan argumentos con elrefmodificador: esas llamadas continúan compilando sin ningún cambio. A diferencia de cambiarrefain, un parámetroref readonlyno permite silenciosamente a los llamadores pasar rvalues (no variables); el compilador emite una advertencia si el argumento no es una variable. Los sitios de llamada existentesrefsiguen siendo válidos.❌ NO PERMITIDO: Cambiar un
inparámetro aref readonlyLas llamadas a sitios que pasan
inargumentos sin el modificadorin(que el compilador permite para parámetrosin) recibirán una advertencia cuando el parámetro cambie aref readonly, ya queref readonlyrequiere que el argumento se pase por referencia. Los autores de llamadas que tratan las advertencias como errores experimentarán un cambio importante en el origen.❌ NO PERMITIDO: Cambio de nombre de un parámetro (incluido el cambio de mayúsculas y minúsculas)
Esto se considera disruptivo por dos motivos:
Interrumpe escenarios de enlace tardío, como la característica de enlace tardío en Visual Basic y dynamic en C#.
Interrumpe la compatibilidad con el código fuente cuando los desarrolladores usan argumentos nombrados.
❌ NO PERMITIDO: Cambiar de un valor de retorno
refa un valor de retornoref readonly❌️ NO PERMITIDO: Cambiar de un valor devuelto
ref readonlya un valor devueltorefen un método virtual o una interfaz❌ NO PERMITIDO: Adición o eliminación del tipo abstract de un miembro
❌ NO PERMITIDO: Eliminación de la palabra clave virtual de un miembro
❌ NO PERMITIDO: Adición de la palabra clave virtual a un miembro
Aunque esto a menudo no es un cambio importante porque el compilador de C# tiende a emitir instrucciones de lenguaje intermedio de callvirt (IL) para llamar a métodos que no son virtuales (
callvirtrealiza una comprobación nula, mientras que una llamada normal no), este comportamiento no es invariable por varias razones:- C# no es el único lenguaje que .NET tiene como destino.
- El compilador de C# intenta cada vez más optimizar
callvirta una llamada normal cada vez que el método de destino no es virtual y probablemente no es null (como un método al que se accede a través del operador de propagación ?. NULL).
Convertir un método en virtual significa que el código del consumidor a menudo acabaría llamándolo no virtual.
❌ NO PERMITIDO: Conversión de un miembro virtual en tipo abstract
Un miembro virtual proporciona una implementación de método que se puede invalidar mediante una clase derivada. Un miembro abstracto no proporciona ninguna implementación y se debe invalidar.
❌ NO PERMITIDO: Agregar la palabra clave sealed a un miembro de interfaz
Agregar
sealeda un miembro de interfaz predeterminado hará que este no sea virtual, impidiendo que se invoque la implementación de un tipo derivado para ese miembro.❌ NO PERMITIDO: agregar un miembro abstracto a un tipo público que tenga constructores accesibles (públicos o protegidos) y que no esté sellado
❌ NO PERMITIDO: Adición o eliminación de la palabra clave static de un miembro
❌ NO PERMITIDO: agregar una sobrecarga que impida una sobrecarga existente y defina un comportamiento diferente.
Esto interrumpe a los clientes existentes que se enlazaron a la sobrecarga anterior. Por ejemplo, si una clase tiene una versión única de un método que acepta un UInt32, un consumidor existente se enlazará correctamente a esa sobrecarga al pasar un valor Int32. Sin embargo, si agrega una sobrecarga que acepte un Int32, al volver a compilar o utilizar la característica de enlace en tiempo de ejecución, el compilador se enlaza ahora a la nueva sobrecarga. Si hay resultados de comportamiento diferentes, esto puede ser un cambio importante.
❓ REQUIERE SENTENCIA: Agregar OverloadResolutionPriorityAttribute a una sobrecarga existente o cambiar su valor de prioridad
La OverloadResolutionPriorityAttribute afecta a la resolución de sobrecargas a nivel de origen: los llamantes que se recompilan podrían resolver en una sobrecarga diferente a la anterior. El uso previsto es agregar el atributo a una nueva y mejor sobrecarga para que el compilador la prefiera sobre las existentes. Agregarlo a una sobrecarga existente o cambiar el valor de prioridad en una sobrecarga que ya tiene atributos puede provocar un cambio disruptivo en el código fuente, ya que los compiladores que vuelvan a compilar el código podrían alterar el comportamiento.
✔️ PERMITIDO: Adición de la anti-constraint
allows ref structcomo restricción a un parámetro de tipo genéricoAgregar
allows ref structamplía los tipos que se pueden usar como argumentos de tipo al permitir tiposref struct. Los autores de llamadas existentes que usan argumentos de tipo no-ref structno se ven afectados. El método o tipo genérico debe seguir las reglas de seguridad ref para todas las instancias de ese parámetro de tipo.❌ NO PERMITIDO: eliminar la
allows ref structanti-restricción de un parámetro de tipo genéricoAl quitar
allows ref struct, se restringen los tipos que los llamantes pueden usar como argumentos de tipo. Cualquier llamador que pase unref structcomo argumento de tipo ya no podrá compilarse.❌ NO PERMITIDO: agregar un constructor a una clase que anteriormente no tenía ningún constructor sin agregar el constructor sin parámetros
❌️ NO PERMITIDO: Agregar readonly a un campo
❌ NO PERMITIDO: Reducción de la visibilidad de un miembro
Esto incluye reducir la visibilidad de un miembro protegido cuando existen constructores accesibles (
publicoprotected) y el tipo no está sellado. Si no es así, se permite reducir la visibilidad de un miembro protegido.Se permite aumentar la visibilidad de un miembro.
❌ NO PERMITIDO: Cambiar el tipo de un miembro
No se puede modificar el valor devuelto de un método o el tipo de una propiedad o campo. Por ejemplo, la firma de un método que devuelve un Object no se puede cambiar para devolver un String, o viceversa.
❌ NO PERMITIDO: Agregar un campo de instancia a una estructura que no tiene campos no públicos
Si un struct solo tiene campos públicos o no tiene campos en absoluto, los autores de llamadas pueden declarar variables locales de ese tipo de estructura sin llamar al constructor de la estructura o inicializar primero el local en
default(T), siempre y cuando todos los campos públicos se establezcan en la estructura antes de su primer uso. Agregar cualquier nuevo campo (público o no público) a tal estructura representa un cambio fundamental en la fuente para los usuarios, ya que el compilador requerirá que se inicialicen los campos adicionales ahora.Además, agregar cualquier campo nuevo (público o no público) a una estructura sin campos o solo con campos públicos es un cambio importante binario en los autores de llamadas que han aplicado
[SkipLocalsInit]a su código. Dado que el compilador no era consciente de estos campos en tiempo de compilación, podía emitir IL que no inicializa completamente la estructura, lo que provocaba que la estructura se creara a partir de datos de pila no inicializados.Si un struct tiene campos no públicos, el compilador ya aplica la inicialización a través del constructor o
default(T)y la adición de nuevos campos de instancia no es un cambio importante.❌ NO PERMITIDO: Desencadenamiento de un evento existente nunca antes desencadenado
Cambios de comportamiento
Ensamblajes
✔️ PERMITIDO: Hacer que un ensamblaje sea portátil cuando todavía se admiten las mismas plataformas
❌ NO PERMITIDO: Cambiar el nombre de un ensamblado
❌ NO PERMITIDO: Cambio de la clave pública de un ensamblado
Propiedades, campos, parámetros y valores devueltos
✔️ ALLOWED: cambio del valor de una propiedad, un campo, un valor devuelto o un parámetro out a un tipo más derivado
Por ejemplo, un método que devuelve un tipo de Object puede devolver una String instancia. (Sin embargo, la firma del método no puede cambiar).
✔️ PERMITIDO: Aumento del intervalo de valores aceptados para una propiedad o parámetro si el miembro no es virtual
Aunque el rango de valores que se pueden pasar al método o que el miembro puede devolver puede expandirse, el tipo de parámetro o miembro no puede cambiar. Por ejemplo, mientras que los valores pasados a un método pueden expandirse de 0 a 124 a 0-255, el tipo de parámetro no puede cambiar de Byte a Int32.
❌ NO PERMITIDO: aumentar el intervalo de valores aceptados para una propiedad o parámetro si el miembro es virtual
Este cambio interrumpe los miembros invalidados existentes, que no funcionarán correctamente para la gama extendida de valores.
❌ NO PERMITIDO: Disminuir el intervalo de valores aceptados para una propiedad o parámetro
❌ NO PERMITIDO: aumentar el intervalo de valores devueltos para una propiedad, un campo, un valor devuelto o un parámetro out
❌ PROHIBIDO: cambiar los valores devueltos para una propiedad, un campo, el valor devuelto de un método o un parámetro out
❌ NO PERMITIDO: Cambio del valor predeterminado de una propiedad, un campo o un parámetro
Cambiar o quitar un valor predeterminado de parámetro no es una interrupción binaria. Eliminar el valor predeterminado de un parámetro es una ruptura del código fuente, y cambiar el valor predeterminado de un parámetro podría dar lugar a una alteración del comportamiento después de la recompilación.
Por este motivo, quitar los valores predeterminados del parámetro es aceptable en el caso específico de "mover" esos valores predeterminados a una nueva sobrecarga de método para eliminar la ambigüedad. Por ejemplo, considere un método
MyMethod(int a = 1)existente. Si introduce una sobrecarga deMyMethodcon dos parámetros opcionalesayb, puede conservar la compatibilidad moviendo el valor predeterminado deaa la nueva sobrecarga. Ahora las dos sobrecargas sonMyMethod(int a)yMyMethod(int a = 1, int b = 2). Este patrón permite aMyMethod()compilar.❌ NO PERMITIDO: Cambio de la precisión de un valor devuelto numérico
❓ REQUIERE EVALUACIÓN: Un cambio en el análisis de la entrada y el inicio de nuevas excepciones (incluso si el comportamiento de análisis no está especificado en la documentación)
❌ NO PERMITIDO: Agregar o quitar tipos de casos de una
uniondeclaraciónAgregar o quitar un tipo de caso de un
uniontipo resulta en una ruptura binaria y una ruptura de código fuente.Las pruebas de coincidencia de patrones ya no son exhaustivas después de agregar un tipo de caso. El compilador marca expresiones de coincidencia de patrones como no exhaustivas. En tiempo de ejecución, los valores inesperados provocan excepciones en tiempo de ejecución. Al quitar un tipo de caso, se quita la declaración de constructor para ese tipo de caso.
Excepciones
✔️ PERMITIDO: lanzar una excepción más derivada que una excepción existente
Dado que la nueva excepción es una subclase de una excepción existente, el código de control de excepciones anterior continúa controlando la excepción. Por ejemplo, en .NET Framework 4, los métodos de creación y recuperación de culturas comenzaron a lanzar una CultureNotFoundException en lugar de una ArgumentException si no se encontró la cultura. Dado que CultureNotFoundException se deriva de ArgumentException, se trata de un cambio aceptable.
✔️ PERMITIDO: Inicio de una excepción más específica que NotSupportedException, NotImplementedException, NullReferenceException
✔️ PERMITIDO: Inicio de una excepción que se considera irrecuperable
Las excepciones irrecuperables no deben capturarse, sino que deben tratarse por un controlador general de alto nivel. Por lo tanto, no se espera que los usuarios tengan código que capture estas excepciones explícitas. Las excepciones irrecuperables son:
✔️ PERMITIDO: Lanzar una nueva excepción en una nueva trayectoria de código
La excepción solo se debe aplicar a una nueva ruta de acceso de código que se ejecuta con nuevos valores de parámetro o estado y que no se puede ejecutar mediante código existente que tenga como destino la versión anterior.
✔️ ALLOWED: eliminación de una excepción para habilitar un comportamiento más sólido o nuevos escenarios
Por ejemplo, un método
Divideque anteriormente solo trataba valores positivos e iniciaba un ArgumentOutOfRangeException de lo contrario, puede cambiarse para admitir tanto valores negativos como positivos sin iniciar una excepción.✔️ ALLOWED: Cambio del texto de un mensaje de error
Los desarrolladores no deben confiar en el texto de los mensajes de error, que también cambian en función de la referencia cultural del usuario.
❌ NO PERMITIDO: Lanzar una excepción en cualquier otro caso no enumerado anteriormente
❌ NO PERMITIDO: Quitar una excepción en cualquier otro caso no enumerado anteriormente
Atributos
✔️ PERMITIDO: Cambiar el valor de un atributo que no es observable
❌ NO PERMITIDO: cambio del valor de un atributo que es observable
❓ REQUIERE SENTENCIA: Quitar un atributo
En la mayoría de los casos, quitar un atributo (como NonSerializedAttribute) es un cambio importante.
Compatibilidad con plataformas
✔️ PERMITIDO: Admisión de una operación en una plataforma que antes no era posible
❌ NO PERMITIDO: No admitir o requerir ahora un Service Pack específico para una operación que estaba admitida en una plataforma
Cambios de implementación internos
❓ REQUIERE EVALUACIÓN: Cambio del área expuesta de un tipo interno
Estos cambios se permiten generalmente, aunque interrumpen la reflexión privada. En algunos casos, cuando las bibliotecas populares de terceros o un gran número de desarrolladores dependen de las API internas, es posible que no se permitan estos cambios.
❓ REQUIERE EVALUACIÓN: Cambio de la implementación interna de un miembro
Estos cambios suelen permitirse, aunque interrumpen la reflexión privada. En algunos casos, cuando el código del cliente depende con frecuencia de la reflexión privada o de dónde el cambio introduce efectos secundarios no deseados, es posible que estos cambios no se permitan.
✔️ PERMITIDO: Mejora del rendimiento de una operación
La capacidad de modificar el rendimiento de una operación es esencial, pero estos cambios pueden interrumpir el código que depende de la velocidad actual de una operación. Esto es especialmente cierto en el código que depende del tiempo de las operaciones asincrónicas. El cambio de rendimiento no debe tener ningún efecto en otro comportamiento de la API en cuestión; De lo contrario, el cambio se interrumpirá.
✔️ PERMITIDO: Indirectamente (y a menudo adversamente) cambiando el rendimiento de una operación
Si el cambio en cuestión no está clasificado como importante por algún otro motivo, esto es aceptable. A menudo, es necesario realizar acciones que incluyan operaciones adicionales o que agreguen nuevas funcionalidades. Esto casi siempre afectará al rendimiento, pero puede ser esencial para que la API en cuestión funcione según lo previsto.
❌ NO PERMITIDO: cambio de una API sincrónica a asincrónica (y viceversa)
Cambios en el código
✔️ PERMITIDO: Se permite la adición de parámetros a un parámetro
❌ NO PERMITIDO: Cambio de una estructura a una clase y viceversa
❌ NO PERMITIDO: Adición de la instrucción checked a un bloque de código
Este cambio puede provocar que el código que previamente se ejecutaba arroje un OverflowException, lo cual no es aceptable.
❌ NO PERMITIDO: Quitar parámetros de un parámetro
❌ NO PERMITIDO: Cambio del tipo de colección de un
paramsparámetroA partir de C# 13, los parámetros de
paramsadmiten tipos de colección que no son matrices, incluyendo Span<T>, ReadOnlySpan<T>, tipos struct o de clase que implementan IEnumerable<T> con un constructor accesible sin parámetros y un método de instanciaAdd, así como tipos de interfaz específicos, tales como IList<T>. Cambiar el tipo de colección de un parámetro existenteparams(por ejemplo, deparams T[]aparams ReadOnlySpan<T>) cambia la firma IL del método y es un cambio rupturista binario. Los llamadores compilados con la versión anterior deben ser recompilados.✔️ ALLOWED: convertir un método de extensión en la sintaxis de miembro del bloque de extensión
A partir de C# 14, puede declarar miembros de extensión mediante bloques
extension, además de la sintaxis anterior de parámetrosthis. Ambas formas generan IL idéntico, por lo que los llamantes no pueden distinguir entre ellos. La conversión de métodos de extensión existentes a la nueva sintaxis de bloque de extensión es binaria y compatible con el origen.❌ NO PERMITIDO: Cambio del orden en el que se desencadenan los eventos
Los desarrolladores pueden esperar razonablemente que los eventos se activen en el mismo orden y el código de desarrollador con frecuencia depende del orden en el que se desencadenan los eventos.
❌ NO PERMITIDO: Eliminación del inicio de un evento en una acción determinada
❌ NO PERMITIDO: Cambio del número de veces que se llaman los eventos dados
❌ NO PERMITIDO: Agregar a FlagsAttribute un tipo de enumeración