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é par défaut dans .NET 8 À 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 ou UIntPtr est utilisé comme int ou uint natif, remplacez le type par nint ou nuint.
    • Si le type IntPtr ou UIntPtr 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 l’expression peut entraîner une modification comportementale, encapsulez-la avec une instruction checked ou unchecked 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 ou UIntPtr est utilisé comme int ou uint natif, remplacez le type par nint ou nuint.
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 ou unchecked 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.

Voir aussi