CA2020 : Empêcher les changements de comportement causés par les opérateurs intégrés d’IntPtr/UIntPtr
Propriété | Value |
---|---|
Identificateur de la règle | CA2020 |
Titre | Empêcher les changements de comportement causés par les opérateurs intégrés d’IntPtr/UIntPtr |
Catégorie | Fiabilité |
Le correctif est cassant ou non cassant | Sans rupture |
Activée par défaut dans .NET 9 | À titre de suggestion |
Cause
Cette règle se déclenche lorsqu’elle détecte un changement de comportement entre .NET 6 et .NET 7 introduit par les nouveaux opérateurs intégrés de IntPtr et UIntPtr.
Description de la règle
Avec la fonctionnalité IntPtr numérique, IntPtr et UIntPtr ont reçu des opérateurs intégrés pour les conversions, les opérations unaires et les opérations binaires. Ces opérateurs peuvent lever une exception en cas de dépassement dans le contexte vérifié ou ne pas en lever dans un contexte non vérifié par rapport aux opérateurs définis par l’utilisateur précédents dans .NET 6 et versions antérieures. Vous pourriez rencontrer ce changement de comportement lors de la mise à niveau vers .NET 7.
Liste des API affectées
Opérateur | Context | Dans .NET 7 | Dans .NET 6 et versions antérieures | Exemple |
---|---|---|---|---|
opérateur +(IntPtr, int) | checked | Lève en cas de dépassement de capacité | Ne lève pas en cas de dépassement de capacité | checked(intPtrVariable + 2); |
opérateur -(IntPtr, int) | checked | Lève en cas de dépassement de capacité | Ne lève pas en cas de dépassement de capacité | checked(intPtrVariable - 2); |
opérateur explicite IntPtr(long) | unchecked | Ne lève pas en cas de dépassement de capacité | Peut lever une exception dans les contextes 32 bits | (IntPtr)longVariable; |
opérateur explicite void*(IntPtr) | checked | lève une exception en cas de dépassement | Ne lève pas en cas de dépassement de capacité | checked((void*)intPtrVariable); |
opérateur explicite IntPtr(void*) | checked | lève une exception en cas de dépassement | Ne lève pas en cas de dépassement de capacité | checked((IntPtr)voidPtrVariable); |
opérateur explicite int(IntPtr) | unchecked | Ne lève pas en cas de dépassement de capacité | Peut lever une exception dans les contextes 64 bits | (int)intPtrVariable; |
opérateur +(UIntPtr, int) | checked | Lève en cas de dépassement de capacité | Ne lève pas en cas de dépassement de capacité | checked(uintPtrVariable + 2); |
opérateur -(UIntPtr, int) | checked | Lève en cas de dépassement de capacité | Ne lève pas en cas de dépassement de capacité | checked(uintPtrVariable - 2); |
opérateur explicite UIntPtr(ulong) | unchecked | Ne lève pas en cas de dépassement de capacité | Peut lever une exception dans les contextes 32 bits | (UIntPtr)uLongVariable |
opérateur explicite uint(UIntPtr) | unchecked | Ne lève pas en cas de dépassement de capacité | Peut lever une exception dans les contextes 64 bits | (uint)uintPtrVariable |
Comment corriger les violations
Examinez votre code pour déterminer si l’expression marquée peut entraîner un changement de comportement et choisissez un moyen approprié de corriger le diagnostic à partir des options suivantes :
Options de correction :
- Si l’expression ne provoque pas de changement de comportement :
- Si le type
IntPtr
ouUIntPtr
est utilisé commeint
ouuint
natif, remplacez le type parnint
ounuint
. - Si le type
IntPtr
ouUIntPtr
est utilisé comme pointeur natif, remplacez le type par le type de pointeur natif correspondant. - Si vous ne pouvez pas modifier le type de la variable, supprimez l’avertissement.
- Si le type
- Si l’expression peut entraîner une modification comportementale, encapsulez-la avec une instruction
checked
ouunchecked
pour conserver le comportement précédent.
Exemple
Violation :
using System;
public unsafe class IntPtrTest
{
IntPtr intPtrVariable;
long longVariable;
void Test ()
{
checked
{
IntPtr result = intPtrVariable + 2; // Warns: Starting with .NET 7 the operator '+' will throw when overflowing in a checked context. Wrap the expression with an 'unchecked' statement to restore the .NET 6 behavior.
result = intPtrVariable - 2; // Starting with .NET 7 the operator '-' will throw when overflowing in a checked context. Wrap the expression with an 'unchecked' statement to restore the .NET 6 behavior.
void* voidPtrVariable = (void*)intPtrVariable; // Starting with .NET 7 the explicit conversion '(void*)IntPtr' will throw when overflowing in a checked context. Wrap the expression with an 'unchecked' statement to restore the .NET 6 behavior.
result = (IntPtr)voidPtrVariable; // Starting with .NET 7 the explicit conversion '(IntPtr)void*' will throw when overflowing in a checked context. Wrap the expression with an 'unchecked' statement to restore the .NET 6 behavior.
}
intPtrVariable = (IntPtr)longVariable; // Starting with .NET 7 the explicit conversion '(IntPtr)Int64' will not throw when overflowing in an unchecked context. Wrap the expression with a 'checked' statement to restore the .NET 6 behavior.
int a = (int)intPtrVariable; // Starting with .NET 7 the explicit conversion '(Int32)IntPtr' will not throw when overflowing in an unchecked context. Wrap the expression with a 'checked' statement to restore the .NET 6 behavior.
}
}
Correctif :
- Si l’expression n’entraîne pas de changement comportemental et que le type
IntPtr
ouUIntPtr
est utilisé commeint
ouuint
natif, remplacez le type parnint
ounuint
.
using System;
public unsafe class IntPtrTest
{
nint intPtrVariable; // type changed to nint
long longVariable;
void Test ()
{
checked
{
nint result = intPtrVariable + 2; // no warning
result = intPtrVariable - 2;
void* voidPtrVariable = (void*)intPtrVariable;
result = (nint)voidPtrVariable;
}
intPtrVariable = (nint)longVariable;
int a = (int)intPtrVariable;
}
}
- Si l’expression peut entraîner une modification comportementale, encapsulez-la avec une instruction
checked
ouunchecked
pour conserver le comportement précédent.
using System;
public unsafe class IntPtrTest
{
IntPtr intPtrVariable;
long longVariable;
void Test ()
{
checked
{
IntPtr result = unchecked(intPtrVariable + 2); // wrap with unchecked
result = unchecked(intPtrVariable - 2);
void* voidPtrVariable = unchecked((void*)intPtrVariable);
result = unchecked((IntPtr)voidPtrVariable);
}
intPtrVariable = checked((IntPtr)longVariable); // wrap with checked
int a = checked((int)intPtrVariable);
}
}
Quand supprimer les avertissements
Si l’expression ne provoque pas de changement de comportement, il est sûr de supprimer un avertissement de cette règle.