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:

  • Cuando se pasaba null para un parámetro de tipo byref sin el modificador ref (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:

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.

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