CA2020: Evitar el cambio de comportamiento causado por los operadores integrados de IntPtr/UIntPtr

Propiedad Value
Identificador de la regla CA2020
Título Evitar el cambio de comportamiento causado por operadores integrados de IntPtr/UIntPtr
Categoría Confiabilidad
La corrección es problemática o no problemática Poco problemático
Habilitado de forma predeterminada en .NET 8 Como sugerencia

Causa

Esta regla se desencadena cuando detecta un cambio de comportamiento entre .NET 6 y .NET 7 introducido por los nuevos operadores integrados de IntPtr y UIntPtr.

Descripción de la regla

Con la característica IntPtr numérico, IntPtr y UIntPtr obtuvieron operadores integrados para conversiones, operaciones unarias y operaciones binarias. Estos operadores pueden generar una excepción en los desbordamientos dentro de un contexto comprobado o pueden no generar una excepción en un contexto no comprobado en comparación con los operadores definidos por el usuario anteriores en .NET 6 y versiones anteriores. Es posible que encuentre este cambio de comportamiento al actualizar a .NET 7.

Lista de API afectadas

Operador Context En .NET 7 En .NET 6 y versiones anteriores Ejemplo
operador +(IntPtr, int) Activado Genera una excepción en los desbordamientos No se produce en los desbordamientos checked(intPtrVariable + 2);
operador -(IntPtr, int) Activado Genera una excepción en los desbordamientos No se produce en los desbordamientos checked(intPtrVariable - 2);
operador explícito IntPtr(long) unchecked No se produce en los desbordamientos Puede generar una excepción en contextos de 32 bits (IntPtr)longVariable;
operador explícito void*(IntPtr) Activado Genera una excepción en los desbordamientos No se produce en los desbordamientos checked((void*)intPtrVariable);
operador explícito IntPtr(void*) Activado Genera una excepción en los desbordamientos No se produce en los desbordamientos checked((IntPtr)voidPtrVariable);
operador explícito int(IntPtr) unchecked No se produce en los desbordamientos Puede generar una excepción en contextos de 64 bits (int)intPtrVariable;
operador +(UIntPtr, int) Activado Genera una excepción en los desbordamientos No genera una excepción en los desbordamientos checked(uintPtrVariable + 2);
operador -(UIntPtr, int) Activado Genera una excepción en los desbordamientos No genera una excepción en los desbordamientos checked(uintPtrVariable - 2);
operador explícito UIntPtr(ulong) unchecked No se produce en los desbordamientos Puede generar una excepción en contextos de 32 bits (UIntPtr)uLongVariable
operador explícito uint(UIntPtr) unchecked No se produce en los desbordamientos Puede generar una excepción en contextos de 64 bits (uint)uintPtrVariable

Cómo corregir infracciones

Examine el código para determinar si la expresión marcada podría provocar un cambio de comportamiento y elegir una manera adecuada de corregir el diagnóstico entre las siguientes opciones:

Opciones de corrección:

  • Si la expresión no provocaría un cambio de comportamiento:
    • Si el tipo IntPtr o UIntPtr se usan como un tipo int o uint nativo, cambie el tipo a nint o nuint.
    • Si el tipo IntPtr o UIntPtr se usan como un puntero nativo, cambie el tipo al tipo de puntero nativo correspondiente.
    • Si no puede cambiar el tipo de la variable, suprima la advertencia.
  • Si la expresión podría provocar un cambio de comportamiento, puede encapsularla con una instrucción checked o unchecked para conservar el comportamiento anterior.

Ejemplo

Infracción:

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

Corrección:

  • En caso de que la expresión no provoque un cambio de comportamiento y el tipo IntPtr o UIntPtr se usa como un tipo int o uint nativo, cambie el tipo a 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;
    }
}
  • Si la expresión podría provocar un cambio de comportamiento, puede encapsularla con una instrucción checked o unchecked para conservar el comportamiento anterior.
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);
    }
}

Cuándo suprimir las advertencias

Si la expresión no provocaría un cambio de comportamiento, es seguro suprimir una advertencia de esta regla.

Vea también