Freigeben über


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 zu Unterbrechungen oder bleibt funktionsfähig Untrennbar
Standardmäßig in .NET 10 aktiviert Als Vorschlag
Anwendbare Sprachen C#

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 im geprüften Kontext bei einem Überlauf einen Fehler auslösen, während sie im ungeprüften Kontext möglicherweise keinen Fehler auslösen. 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) überprüft Löst bei Überläufen aus Löst bei Überläufen keinen Fehler aus checked(intPtrVariable + 2);
Operator -(IntPtr, int) überprüft Wirft bei Überläufen Wirft bei Überläufen keinen Fehler checked(intPtrVariable - 2);
Expliziter Operator IntPtr(long) ungeprüft Löst bei Überläufen keinen Fehler aus Kann in 32-Bit-Kontexten einen Fehler auslösen (IntPtr)longVariable;
Expliziter Operator void*(IntPtr) überprüft Löst bei Überläufen aus Löst bei Überläufen keinen Fehler aus checked((void*)intPtrVariable);
expliziter Operator IntPtr(void*) überprüft Löst bei Überläufen einen Fehler aus Löst bei Überläufen keinen Fehler aus checked((IntPtr)voidPtrVariable);
Expliziter Operator int(IntPtr) nicht ausgewählt Wird bei Überlauf nicht ausgelöst Kann in 64-Bit-Kontexten einen Fehler auslösen (int)intPtrVariable;
Operator +(UIntPtr, int) überprüft Wirft bei Überläufen Löst bei Überläufen keinen Fehler aus checked(uintPtrVariable + 2);
Operator -(UIntPtr, int) überprüft Wirft bei Überläufen Löst bei Überläufen keinen Fehler aus checked(uintPtrVariable - 2);
Expliziter Operator UIntPtr(ulong) nicht ausgewählt Löst bei Überläufen keinen Fehler aus Kann in 32-Bit-Kontexten einen Fehler auslösen (UIntPtr)uLongVariable
Expliziter Operator uint(UIntPtr) nicht ausgewählt Löst bei Überläufen keinen Fehler aus Kann in 64-Bit-Kontexten einen Fehler auslösen (uint)uintPtrVariable

So beheben Sie Verstöße

Untersuchen Sie Ihren Code, um festzustellen, ob der gekennzeichnete Ausdruck eine Verhaltensänderung verursachen könnte, und wählen Sie aus den folgenden Optionen eine geeignete Methode zur Behebung der Diagnose:

Reparaturoptionen

  • Wenn der Ausdruck keine Verhaltensänderung verursacht, gilt Folgendes:
    • 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 eine Verhaltensänderung verursachen könnte, umschließen Sie ihn mit einer checked- oder unchecked-Anweisung, um das bisherige Verhalten zu erhalten.

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 keine Verhaltensänderung verursacht und der IntPtr- oder UIntPtr-Typ als nativer int- oder uint-Typ 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 eine Verhaltensänderung verursachen könnte, umschließen Sie ihn mit einer checked- oder unchecked-Anweisung, um das vorherige Verhalten zu erhalten.
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 keine Verhaltensänderung verursacht, kann ein Warnhinweis von dieser Regel ohne Risiko unterdrückt werden.

Siehe auch