Condividi tramite


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à
La correzione causa un'interruzione o meno Non rompente
Abilitato per impostazione predefinita in .NET 10 Come suggerimento
Linguaggi applicabili C#

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à Numeric IntPtr e UIntPtr hanno acquisito operatori predefiniti per conversioni, operazioni unarie e operazioni binarie. Questi operatori potrebbero generare un'eccezione in caso di overflow in un contesto controllato, oppure potrebbero non generarla in un contesto non controllato, rispetto agli operatori personalizzati delle versioni precedenti a .NET 6. È 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) verificato Genera quando si verifica un overflow Non genera un'eccezione quando si verifica un overflow checked(intPtrVariable + 2);
operator -(IntPtr, int) controllato Genera quando si verifica un overflow Non genera un'eccezione quando si verifica un overflow checked(intPtrVariable - 2);
Operatore explicit IntPtr(long) non selezionato Non genera un'eccezione quando si verifica un overflow Può lanciare eccezioni in contesti a 32 bit (IntPtr)longVariable;
Operatore explicit void*(IntPtr) verificato genera quando si verifica un overflow Non genera un'eccezione quando si verifica un overflow checked((void*)intPtrVariable);
Operatore explicit IntPtr(void*) verificato genera quando si verifica un overflow Non genera eccezioni in caso di overflow checked((IntPtr)voidPtrVariable);
operatore esplicito int(IntPtr) non selezionato Non genera un'eccezione quando si verifica un overflow Può generare un'eccezione in contesti a 64 bit (int)intPtrVariable;
operator +(UIntPtr, int) verificato Genera quando si verifica un overflow Non genera un'eccezione quando si verifica un overflow checked(uintPtrVariable + 2);
operator -(UIntPtr, int) verificato Genera quando si verifica un overflow Non genera un'eccezione quando si verifica un overflow checked(uintPtrVariable - 2);
Operatore esplicito UIntPtr(ulong) non selezionato Non genera un'eccezione quando si verifica un overflow Può lanciare eccezioni in contesti a 32 bit (UIntPtr)uLongVariable
Operatore esplicito uint(UIntPtr) non selezionato Non genera un'eccezione quando si verifica un overflow Può generare un'eccezione nei 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 tipo IntPtr o UIntPtr è utilizzato come int o uint nativo, cambiare il tipo in nint o nuint.
    • Se il tipo IntPtr o UIntPtr viene utilizzato come puntatore nativo, modificare il tipo nel corrispondente tipo di puntatore nativo.
    • 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 provoca una modifica comportamentale e IntPtr o UIntPtr tipo è 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