Cambios en las excepciones de las API de invocación de reflexión
Las excepciones que se inician al llamar a las API de invocación de reflexión han cambiado.
Comportamiento anterior
Anteriormente, cuando un método invocado que devuelve un valor por referencia devolvía
null
, se iniciaba una excepción NullReferenceException.En el caso de los constructores, se iniciaban las siguientes excepciones:
- Excepciones transitorias, lo que incluye OutOfMemoryException.
- OverflowException cuando se pasaba un valor negativo para el parámetro length de una matriz.
Cuando se pasaba
null
para un parámetro de tipo byref sin el modificadorref
(es decir, se pasaba por valor), no se iniciaba ninguna excepción y el runtime sustituía un valor predeterminado por el valor NULL.
Comportamiento nuevo
A partir de .NET 7:
En lugar de iniciar la excepción de origen (lo que incluye NullReferenceException y OutOfMemoryException, que se mencionan en Comportamiento anterior), se inicia TargetInvocationException en todos los casos después de validar los parámetros iniciales
Invoke()
. La excepción interna contiene la excepción de origen.NotSupportedException se inicia cuando se pasa
null
para un parámetro de tipo byref si el parámetro se declara como "por valor" (es decir, no tiene ningún modificadorref
). En el caso relacionado en que el parámetro se pasa por referencia (es decir, tiene el modificadorref
), el comportamiento anterior y nuevo son iguales: se inicia una excepción NotSupportedException.
Versión introducida
.NET 7
Tipo de cambio importante
Este cambio puede afectar a la compatibilidad binaria.
Motivo del cambio
El inicio de TargetInvocationException en lugar de la excepción de origen hace que la experiencia sea más coherente. Coloca correctamente en capas las excepciones causadas por la validación de los parámetros entrantes (que no se encapsulan con TargetInvocationException) frente a las excepciones iniciadas debido a la implementación del método de destino (que se encapsulan). Unas reglas coherentes proporcionan experiencias más coherentes en diferentes implementaciones de CLR y de las API Invoke
.
El cambio para iniciar NotSupportedException cuando se pasa un tipo byref a una API Invoke()
corrige una omisión de la implementación original, que no iniciaba la excepción. La implementación original daba la impresión de que los tipos ref struct
son compatibles con las API Invoke()
, cuando no lo son. Dado que las API actuales Invoke()
usan System.Object para los tipos de parámetros y a un tipo ref struct
no se le puede aplicar conversión boxing en System.Object, se trata de un escenario no admitido.
Acción recomendada
Si no usa BindingFlags.DoNotWrapExceptions al llamar a Invoke()
y tiene instrucciones catch
en torno a las API Invoke()
en excepciones distintas de TargetInvocationException, considere la posibilidad de cambiar o quitar esas instrucciones catch
. Las demás excepciones ya no se inician como resultado de la invocación. Pero si detecta excepciones de la validación de argumentos que se produce antes de intentar invocar al método de destino, debe mantener esas instrucciones catch
. Los argumentos no válidos que se validan antes de intentar invocar se inician sin encapsularse con TargetInvocationException y no cambian la semántica.
Considere la posibilidad de usar BindingFlags.DoNotWrapExceptions para que TargetInvocationException nunca se inicie. En este caso, la excepción de origen no se encapsula con TargetInvocationException. En la mayoría de los casos, no encapsular la excepción mejora las posibilidades de diagnosticar el problema real, ya que no todas las herramientas de notificación de excepciones muestran la excepción interna. Además, al usar BindingFlags.DoNotWrapExceptions, se inician las mismas excepciones que al llamar al método directamente (sin reflexión). Esto es deseable en la mayoría de los casos, ya que la decisión de usar o no reflexión puede ser arbitraria o un detalle de implementación que no necesita exponerse al autor de la llamada.
En el caso raro de que necesite pasar un valor predeterminado a un método mediante la reflexión que contiene un parámetro de tipo byref que se pasa "por valor", puede agregar un método encapsulador que omita el parámetro y llame al método de destino con un valor predeterminado para ese parámetro.
API afectadas
- System.Reflection.MethodBase.Invoke(Object, Object[])
- System.Reflection.MethodBase.Invoke(Object, BindingFlags, Binder, Object[], CultureInfo)
- System.Reflection.ConstructorInfo.Invoke(Object[])
- System.Reflection.ConstructorInfo.Invoke(BindingFlags, Binder, Object[], CultureInfo)
- System.Reflection.PropertyInfo.GetValue(Object)
- System.Reflection.PropertyInfo.GetValue(Object, Object[])
- System.Reflection.PropertyInfo.GetValue(Object, BindingFlags, Binder, Object[], CultureInfo)
- System.Reflection.PropertyInfo.SetValue(Object, Object)
- System.Reflection.PropertyInfo.SetValue(Object, Object, Object[])
- System.Reflection.PropertyInfo.SetValue(Object, Object, BindingFlags, Binder, Object[], CultureInfo)
- System.Reflection.Emit.DynamicMethod.Invoke(Object, BindingFlags, Binder, Object[], CultureInfo)
- System.Activator.CreateInstance(Type, Object[])
- System.Activator.CreateInstance(Type, Object[], Object[])
- System.Activator.CreateInstance(Type, BindingFlags, Binder, Object[], CultureInfo)
- System.Activator.CreateInstance(Type, BindingFlags, Binder, Object[], CultureInfo, Object[])
- System.Activator.CreateInstance(String, String, Boolean, BindingFlags, Binder, Object[], CultureInfo, Object[])
Comentarios
https://aka.ms/ContentUserFeedback.
Próximamente: A lo largo de 2024 iremos eliminando gradualmente GitHub Issues como mecanismo de comentarios sobre el contenido y lo sustituiremos por un nuevo sistema de comentarios. Para más información, vea:Enviar y ver comentarios de