Modifications apportées aux exceptions de l’API d’appel de réflexion

Les exceptions levées lors de l’appel des API d’appel de réflexion ont changé.

Comportement précédent

  • Auparavant, lorsqu’une méthode appelée qui retourne une valeur par référence retournait null, un NullReferenceException était levé.

  • Pour les constructeurs, les exceptions suivantes ont été levées :

  • Lorsque null a été passé pour un paramètre de type byref sans le modificateur ref (autrement dit, passé par valeur), aucune exception n’a été levée, et le runtime a remplacé une valeur par défaut pour la valeur Null.

Nouveau comportement

À compter de .NET 7 :

  • Au lieu de lever l’exception d’origine (y compris NullReferenceException et OutOfMemoryException mentionné dans le Comportement précédent), TargetInvocationException est levé dans tous les cas après la validation des paramètres initiaux Invoke(). L’exception interne contient l’exception d’origine.

  • NotSupportedException est levé lorsque null est passé pour un paramètre de type byref lorsque le paramètre est déclaré comme étant « par valeur » (autrement dit, il n’a aucun modificateur ref). Pour le cas associé lorsque le paramètre est passé par référence (autrement dit, lorsqu’il a le modificateur ref), le comportement précédent et nouveau est le même : un NotSupportedException est levé.

Version introduite

.NET 7

Type de changement cassant

Ce changement peut affecter la compatibilité binaire.

Raison du changement

La levée de TargetInvocationException au lieu de l’exception d’origine rend l’expérience plus cohérente. Elle couche correctement les exceptions provoquées par la validation des paramètres entrants (qui ne sont pas encapsulés avec TargetInvocationException) par rapport aux exceptions levées en raison de l’implémentation de la méthode cible (qui sont encapsulées). Les règles cohérentes fournissent des expériences plus cohérentes entre différentes implémentations du CLR et des API Invoke.

La modification à lever NotSupportedException lorsqu’un type de type byref est transmis à une API Invoke() corrige une supervision de l’implémentation d’origine, qui n’a pas levée. L’implémentation d’origine a donné l’apparence que les types ref struct sont pris en charge par les API Invoke(), lorsqu’ils ne le sont pas. Étant donné que les API actuelles Invoke() utilisent System.Object pour les types de paramètres et qu’un type ref struct ne peut pas être boxé vers System.Object, il s’agit d’un scénario non pris en charge.

Si vous n’utilisez pas BindingFlags.DoNotWrapExceptions lors de l’appel de Invoke(), et que vous avez des instructions catch autour des API Invoke() pour les exceptions autres que TargetInvocationException, envisagez de modifier ou de supprimer ces instructions catch. Les autres exceptions ne seront plus levées suite à l’appel. Toutefois, si vous interceptez des exceptions à partir de la validation d’arguments qui se produisent avant de tenter d’appeler la méthode cible, vous devez conserver ces instructions catch. Les arguments non valides qui sont validés avant de tenter d’appeler sont levés sans être encapsulés avec TargetInvocationException et ne changent pas la sémantique.

Envisagez d’utiliser BindingFlags.DoNotWrapExceptions pour que TargetInvocationException ne soit jamais être levé. Dans ce cas, l’exception d’origine ne sera pas encapsulée par un TargetInvocationException. Dans la plupart des cas, le fait de ne pas encapsuler l’exception améliore les chances de diagnostiquer le problème réel, car tous les outils de création de rapports d’exceptions n’affichent pas l’exception interne. En outre, en utilisant BindingFlags.DoNotWrapExceptions, les mêmes exceptions seront levées lors de l’appel de la méthode directement (sans réflexion). Cela est souhaitable dans la plupart des cas, car le choix de déterminer si la réflexion est utilisée ou non peut être arbitraire ou un détail d’implémentation qui n’a pas besoin d’être exposé à l’appelant.

Dans les rares cas où vous devez passer une valeur par défaut vers une méthode par le biais d’une réflexion qui contient un paramètre de type byref passé « par valeur », vous pouvez ajouter une méthode wrapper qui omet le paramètre et appelle la méthode cible avec une valeur par défaut pour ce paramètre.

API affectées