分享方式:


CA2020:防止 IntPtr/UIntPtr 內建運算子所造成的行為變更

屬性
規則識別碼 CA2020
標題 防止 IntPtr/UIntPtr 內建運算子所造成的行為變更
類別 可靠性
修正程式是中斷或非中斷 不中斷
預設在 .NET 8 中啟用 建議

原因

當此規則偵測到 和 UIntPtr 的新內建運算子 IntPtr 所引進的 .NET 6 與 .NET 7 之間的行為變更時,就會引發此規則。

檔案描述

使用數值 IntPtr 功能 IntPtrUIntPtr 取得轉換、一元運算和二進位運算的內建運算子。 相較于 .NET 6 和舊版中先前的使用者定義運算子,這些運算子可能會在檢查內容中溢位時擲回,或可能不會在未核取的內容中擲回。 升級至 .NET 7 時,可能會遇到此行為變更。

受影響的 API 清單

運算子 上下文 在 .NET 7 中 在 .NET 6 和更早版本中 範例
operator +(IntPtr, int) 已選取 溢位時擲回 當溢位時不會擲回 checked(intPtrVariable + 2);
operator -(IntPtr, int) 已選取 溢位時擲回 當溢位時不會擲回 checked(intPtrVariable - 2);
explicit 運算子 IntPtr(long) 取消選取 當溢位時不會擲回 可以在 32 位內容中擲回 (IntPtr)longVariable;
explicit operator void*(IntPtr) 已選取 溢位時擲回 當溢位時不會擲回 checked((void*)intPtrVariable);
explicit 運算子 IntPtr(void*) 已選取 溢位時擲回 當溢位時不會擲回 checked((IntPtr)voidPtrVariable);
explicit operator int(IntPtr) 取消選取 當溢位時不會擲回 可以在 64 位內容中擲回 (int)intPtrVariable;
operator +(UIntPtr, int) 已選取 溢位時擲回 當溢位時不會擲回 checked(uintPtrVariable + 2);
operator -(UIntPtr, int) 已選取 溢位時擲回 當溢位時不會擲回 checked(uintPtrVariable - 2);
explicit 運算子 UIntPtr(ulong) 取消選取 當溢位時不會擲回 可以在 32 位內容中擲回 (UIntPtr)uLongVariable
explicit 運算子 uint(UIntPtr) 取消選取 當溢位時不會擲回 可以在 64 位內容中擲回 (uint)uintPtrVariable

如何修正違規

檢查您的程式碼,以判斷標幟的運算式是否可能導致行為變更,並選擇適當的方法來修正下列選項的診斷:

修正選項:

  • 如果運算式不會造成行為變更:
    • IntPtr如果使用 或 UIntPtr 型別做為原生 intuint ,請將類型變更為 nintnuint
    • IntPtr如果使用 或 UIntPtr 型別做為原生指標,請將類型變更為對應的原生指標類型。
    • 如果您無法變更變數的類型,請隱藏警告。
  • 如果運算式可能會造成行為變更,請以 checkedunchecked 語句包裝它,以保留先前的行為。

範例

違規

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

修正

  • 如果運算式不會造成行為變更, IntPtr 且 或 UIntPtr 類型會當做原生 intuint 使用,請將類型變更為 nintnuint
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;
    }
}
  • 如果運算式可能會造成行為變更,請以 checkedunchecked 語句包裝它,以保留先前的行為。
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);
    }
}

隱藏警告的時機

如果運算式不會造成行為變更,則隱藏來自此規則的警告是安全的。

另請參閱