CA2020: Zabránění změnám chování způsobeným integrovanými operátory IntPtr/UIntPtr

Vlastnost Hodnota
ID pravidla CA2020
Název Zabránění změnám chování způsobeným integrovanými operátory IntPtr/UIntPtr
Kategorie Spolehlivost
Oprava, která může být destruktivní nebo nedestruktivní Nezlomitelný
Povoleno ve výchozím nastavení v .NET 10 Jako návrh
Příslušné jazyky jazyk C#

Příčina

Toto pravidlo se aktivuje, když zjistí změnu chování mezi .NET 6 a .NET 7 zavedenými novými integrovanými operátory IntPtr a UIntPtr.

Popis pravidla

S numerickou funkcíIntPtr IntPtr a UIntPtr získali integrované operátory pro převody, unární operace a binární operace. Tyto operátory můžou vyvolat přetečení v rámci kontrolovaného kontextu nebo nemusí vyvolat nezaškrtnutý kontext v porovnání s předchozími uživatelem definovanými operátory v .NET 6 a starších verzích. K této změně chování může dojít při upgradu na .NET 7.

Seznam ovlivněných rozhraní API

Operátor Kontext V .NET 7 V .NET 6 a starších verzích Příklad
operator +(IntPtr, int) zkontrolováno Vyvolá při přetečení Nepřetéká při přetečení checked(intPtrVariable + 2);
operátor -(IntPtr, int) zkontrolováno Vyvolá při přetečení Nepřetéká při přetečení checked(intPtrVariable - 2);
explicitní operátor IntPtr(long) nezaškrtnuto Nepřetéká při přetečení Může vyvolat 32bitové kontexty (IntPtr)longVariable;
explicitní operátor void*(IntPtr) zkontrolováno vyvolá při přetečení Nepřetéká při přetečení checked((void*)intPtrVariable);
explicitní operátor IntPtr(void*) zkontrolováno vyvolá při přetečení Nepřetéká při přetečení checked((IntPtr)voidPtrVariable);
explicitní operátor int(IntPtr) nezaškrtnuto Nepřetéká při přetečení Může vyvolat 64bitové kontexty (int)intPtrVariable;
operator +(UIntPtr, int) zkontrolováno Vyvolá při přetečení Nepoužívá výjimky při přetečení checked(uintPtrVariable + 2);
operátor -(UIntPtr, int) zkontrolováno Vyvolá při přetečení Při přetečení se nehodí checked(uintPtrVariable - 2);
explicitní operátor UIntPtr(ulong) nezaškrtnuto Při přetečení se nehodí Může vyvolat 32bitové kontexty (UIntPtr)uLongVariable
explicitní operátor uint(UIntPtr) nezaškrtnuto Nepřetéká při přetečení Může vyvolat 64bitové kontexty (uint)uintPtrVariable

Jak opravit porušení

Prozkoumejte kód a zjistěte, jestli označený výraz může vyvolat změnu chování, a zvolte vhodný způsob, jak řešit diagnostiku z následujících možností:

Možnosti opravy:

  • Pokud výraz nezpůsobí změnu chování:
    • IntPtr Pokud se typ UIntPtr používá jako nativní int nebo uint, změňte typ na nint nebo nuint.
    • IntPtr Pokud se typ UIntPtr používá jako nativní ukazatel, změňte typ na odpovídající nativní typ ukazatele.
    • Pokud nemůžete změnit typ proměnné, potlačit upozornění.
  • Pokud by výraz mohl způsobit změnu chování, zabalte ho pomocí příkazu checked nebo unchecked, abyste zachovali předchozí chování.

Příklad

Porušení:

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

Oprava:

  • Pokud by výraz nezpůsobil změnu chování a typ IntPtr nebo UIntPtr by byl použit jako nativní int nebo uint, změňte typ na nint nebo 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;
    }
}
  • Pokud by výraz mohl způsobit změnu chování, obalte ho příkazem checked nebo unchecked, abyste zachovali předchozí chování.
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);
    }
}

Kdy potlačit upozornění

Pokud výraz nezpůsobí změnu chování, je bezpečné potlačit upozornění z tohoto pravidla.

Viz také