CA2020:防止 IntPtr/UIntPtr 的内置运算符导致的行为更改

属性
规则 ID CA2020
标题 防止 IntPtr/UIntPtr 的内置运算符导致的行为更改
类别 可靠性
修复是中断修复还是非中断修复 非中断
在 .NET 8 中默认启用 作为建议

原因

当它检测到 IntPtrUIntPtr 的新内置运算符引入的 .NET 6 和 .NET 7 之间的行为更改时,会触发此规则。

规则说明

使用数值 IntPtr 功能IntPtrUIntPtr 获得了用于转换、一元运算和二元运算的内置运算符。 与 .NET 6 及更早版本中的以前用户定义的运算符相比,这些运算符可能在已检查上下文中溢出时引发,也可能不会在未检查上下文中引发。 升级到 .NET 7 时,可能会遇到此行为更改。

受影响的 API 的列表

运算符 上下文 在 .NET 7 中 在 .NET 6 及更早版本中 示例
运算符 +(IntPtr, int) checked 溢出时引发 溢出时不会引发 checked(intPtrVariable + 2);
运算符 -(IntPtr, int) checked 溢出时引发 溢出时不会引发 checked(intPtrVariable - 2);
显式运算符 IntPtr(long) unchecked 溢出时不会引发 可以在 32 位上下文中引发 (IntPtr)longVariable;
显示运算符 void*(IntPtr) checked 溢出时引发 溢出时不会引发 checked((void*)intPtrVariable);
显式运算符 IntPtr(void*) checked 溢出时引发 溢出时不会引发 checked((IntPtr)voidPtrVariable);
显式运算符 int(IntPtr) unchecked 溢出时不会引发 可以在 64 位上下文中引发 (int)intPtrVariable;
运算符 +(UIntPtr, int) checked 溢出时引发 溢出时不会引发 checked(uintPtrVariable + 2);
运算符 -(UIntPtr, int) checked 溢出时引发 溢出时不会引发 checked(uintPtrVariable - 2);
显式运算符 UIntPtr(ulong) unchecked 溢出时不会引发 可以在 32 位上下文中引发 (UIntPtr)uLongVariable
显式运算符 uint(UIntPtr) unchecked 溢出时不会引发 可以在 64 位上下文中引发 (uint)uintPtrVariable

如何解决冲突

检查代码以确定标记的表达式是否可能导致行为更改,并从以下选项中选择适当的方法来修复诊断:

修复选项:

  • 如果表达式不会导致行为更改:
    • 如果 IntPtrUIntPtr 类型用作本机 intuint,则将类型更改为 nintnuint
    • 如果 IntPtrUIntPtr 类型用作本机指针,请将类型更改为相应的本机指针类型。
    • 如果无法更改变量的类型,请禁止显示警告。
  • 如果表达式可能导致行为更改,请使用 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.
    }
}

修复:

  • 如果表达式不会导致行为更改,并且 IntPtrUIntPtr 类型用作本机 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);
    }
}

何时禁止显示警告

如果表达式不会导致行为更改,可以安全地禁止显示此规则中的警告。

另请参阅