CA2020: Verhindern von Verhaltensänderungen durch integrierte Operatoren von IntPtr/UIntPtr

Eigenschaft Wert
Regel-ID CA2020
Titel Verhindern von Verhaltensänderungen durch integrierte Operatoren von IntPtr/UIntPtr
Kategorie Zuverlässigkeit
Fix führt oder führt nicht zur Unterbrechung Nicht unterbrechend
Standardmäßig in .NET 8 aktiviert Als Vorschlag

Ursache

Diese Regel wird ausgelöst, wenn sie eine Verhaltensänderung zwischen .NET 6 und .NET 7 erkennt, die von den neuen integrierten Operatoren von IntPtr und UIntPtr verursacht wurde.

Regelbeschreibung

Mit dem numerischen IntPtr-Feature haben IntPtr und UIntPtr integrierte Operatoren für Konvertierungen, unäre Vorgänge und binäre Vorgänge erhalten. Im Vergleich zu den früheren benutzerdefinierten Operatoren in .NET 6 und früheren Versionen können diese Operatoren bei einem Überlauf im geprüften Kontext einen Fehler verursachen oder im ungeprüften Kontext keinen Fehler verursachen. Diese Verhaltensänderung kann beim Upgrade auf .NET 7 auftreten.

Liste der betroffenen APIs

Operator Kontext In .NET 7 In .NET 6 und früher Beispiel
Operator +(IntPtr, int) checked Löst bei Überläufen einen Fehler aus Löst bei Überläufen keinen Fehler aus checked(intPtrVariable + 2);
Operator -(IntPtr, int) checked Löst bei Überläufen einen Fehler aus Löst bei Überläufen keinen Fehler aus checked(intPtrVariable - 2);
Expliziter Operator IntPtr(long) unchecked Löst bei Überläufen keinen Fehler aus Kann in 32-Bit-Kontexten einen Fehler auslösen (IntPtr)longVariable;
Expliziter Operator void*(IntPtr) checked Löst bei Überläufen einen Fehler aus Löst bei Überläufen keinen Fehler aus checked((void*)intPtrVariable);
Expliziter Operator IntPtr(void*) checked Löst bei Überläufen einen Fehler aus Löst bei Überläufen keinen Fehler aus checked((IntPtr)voidPtrVariable);
Expliziter Operator int(IntPtr) unchecked Löst bei Überläufen keinen Fehler aus Kann in 64-Bit-Kontexten einen Fehler auslösen (int)intPtrVariable;
Operator +(UIntPtr, int) checked Löst bei Überläufen einen Fehler aus Löst bei Überläufen keinen Fehler aus checked(uintPtrVariable + 2);
Operator -(UIntPtr, int) checked Löst bei Überläufen einen Fehler aus Löst bei Überläufen keinen Fehler aus checked(uintPtrVariable - 2);
Expliziter Operator UIntPtr(ulong) unchecked Löst bei Überläufen keinen Fehler aus Kann in 32-Bit-Kontexten einen Fehler auslösen (UIntPtr)uLongVariable
Expliziter Operator uint(UIntPtr) unchecked Löst bei Überläufen keinen Fehler aus Kann in 64-Bit-Kontexten einen Fehler auslösen (uint)uintPtrVariable

Behandeln von Verstößen

Untersuchen Sie Ihren Code darauf, ob der gekennzeichnete Ausdruck einen Behavior Change verursachen könnte, und wählen Sie aus den folgenden Optionen eine geeignete Methode zum Behandeln des Problems aus:

Problembehandlungsoptionen:

  • Wenn der Ausdruck keinen Behavior Change verursacht, können Sie Folgendes tun:
    • Wenn der IntPtr- oder UIntPtr-Typ als nativer int- oder uint-Operator verwendet wird, ändern Sie den Typ in nint oder nuint.
    • Wenn der IntPtr- oder UIntPtr-Typ als nativer Zeiger verwendet wird, ändern Sie den Typ in den entsprechenden nativen Zeigertyp.
    • Wenn Sie den Typ der Variablen nicht ändern können, unterdrücken Sie die Warnung.
  • Wenn der Ausdruck einen Behavior Change verursachen könnte, umschließen Sie ihn mit einer checked- oder unchecked-Anweisung, um das vorherige Verhalten beizubehalten.

Beispiel

Verstoß:

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

Korrektur:

  • Wenn der Ausdruck keinen Behavior Change verursacht und der IntPtr- oder UIntPtr-Typ als nativer int- oder uint-Operator verwendet wird, ändern Sie den Typ in nint oder 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;
    }
}
  • Wenn der Ausdruck einen Behavior Change verursachen könnte, umschließen Sie ihn mit einer checked- oder unchecked-Anweisung, um das vorherige Verhalten beizubehalten.
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);
    }
}

Wann sollten Warnungen unterdrückt werden?

Wenn der Ausdruck keinen Behavior Change verursacht, können Sie eine Warnung dieser Regel ohne Sicherheitsrisiko unterdrücken.

Siehe auch