CA2020: Impedire la modifica comportamentale causata dagli operatori predefiniti di IntPtr/UIntPtr

Proprietà valore
ID regola CA2020
Title Impedire la modifica comportamentale causata dagli operatori predefiniti di IntPtr/UIntPtr
Categoria Affidabilità
Correzione che causa un'interruzione o un'interruzione Nessuna interruzione
Abilitato per impostazione predefinita in .NET 8 Come suggerimento

Causa

Questa regola viene attivata quando rileva una modifica comportamentale tra .NET 6 e .NET 7 introdotta dai nuovi operatori predefiniti di IntPtr e UIntPtr.

Descrizione regola

Con la funzionalitàIntPtr Numeric IntPtr e UIntPtr sono stati ottenuti operatori predefiniti per conversioni, operazioni unarie e operazioni binarie. Questi operatori potrebbero generare un'eccezione quando l'overflow all'interno del contesto controllato o potrebbe non generare un contesto deselezionato rispetto agli operatori definiti dall'utente precedenti in .NET 6 e versioni precedenti. È possibile che si verifichi questa modifica comportamentale durante l'aggiornamento a .NET 7.

Elenco delle API interessate

Operatore Contesto In .NET 7 In .NET 6 e versioni precedenti Esempio
operator +(IntPtr, int) checked Genera quando si verifica un overflow Non genera un'eccezione quando si verifica un overflow checked(intPtrVariable + 2);
operator -(IntPtr, int) checked Genera quando si verifica un overflow Non genera un'eccezione quando si verifica un overflow checked(intPtrVariable - 2);
Operatore explicit IntPtr(long) unchecked Non genera un'eccezione quando si verifica un overflow Può generare in contesti a 32 bit (IntPtr)longVariable;
Operatore explicit void*(IntPtr) checked genera quando si verifica un overflow Non genera un'eccezione quando si verifica un overflow checked((void*)intPtrVariable);
Operatore explicit IntPtr(void*) checked genera quando si verifica un overflow Non genera un'eccezione quando si verifica un overflow checked((IntPtr)voidPtrVariable);
Operatore explicit int(IntPtr) unchecked Non genera un'eccezione quando si verifica un overflow Può generare un'eccezione in contesti a 64 bit (int)intPtrVariable;
operator +(UIntPtr, int) checked Genera quando si verifica un overflow Non genera un'eccezione quando si verifica un overflow checked(uintPtrVariable + 2);
operator -(UIntPtr, int) checked Genera quando si verifica un overflow Non genera un'eccezione quando si verifica un overflow checked(uintPtrVariable - 2);
Operatore explicit UIntPtr(ulong) unchecked Non genera un'eccezione quando si verifica un overflow Può generare in contesti a 32 bit (UIntPtr)uLongVariable
Operatore explicit uint(UIntPtr) unchecked Non genera un'eccezione quando si verifica un overflow Può generare un'eccezione in contesti a 64 bit (uint)uintPtrVariable

Come correggere le violazioni

Esaminare il codice per determinare se l'espressione contrassegnata potrebbe causare una modifica comportamentale e scegliere un modo appropriato per correggere la diagnostica dalle opzioni seguenti:

Opzioni di correzione:

  • Se l'espressione non provocherebbe una modifica comportamentale:
    • Se il IntPtr tipo o UIntPtr viene usato come nativo int o uint, modificare il tipo in nint o nuint.
    • Se il IntPtr tipo o UIntPtr viene usato come puntatore nativo, modificare il tipo nel tipo di puntatore nativo corrispondente.
    • Se non è possibile modificare il tipo della variabile, eliminare l'avviso.
  • Se l'espressione potrebbe causare una modifica comportamentale, eseguire il wrapping con un'istruzione checked o unchecked per mantenere il comportamento precedente.

Esempio

Violazione:

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.
    }
}

Correzione:

  • Se l'espressione non provocherebbe una modifica comportamentale e il IntPtr tipo o UIntPtr viene usato come nativo int o uint, modificare il tipo in nint o 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;
    }
}
  • Se l'espressione potrebbe causare una modifica comportamentale, eseguire il wrapping con un'istruzione checked o unchecked per mantenere il comportamento precedente.
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);
    }
}

Quando eliminare gli avvisi

Se l'espressione non provocherebbe una modifica comportamentale, è possibile eliminare un avviso da questa regola.

Vedi anche