CA2020:防止 IntPtr/UIntPtr 的内置运算符导致的行为更改
属性 | 值 |
---|---|
规则 ID | CA2020 |
标题 | 防止 IntPtr/UIntPtr 的内置运算符导致的行为更改 |
类别 | 可靠性 |
修复是中断修复还是非中断修复 | 非中断 |
在 .NET 8 中默认启用 | 作为建议 |
原因
当它检测到 IntPtr 和 UIntPtr 的新内置运算符引入的 .NET 6 和 .NET 7 之间的行为更改时,会触发此规则。
规则说明
使用数值 IntPtr 功能,IntPtr 和 UIntPtr 获得了用于转换、一元运算和二元运算的内置运算符。 与 .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 |
如何解决冲突
检查代码以确定标记的表达式是否可能导致行为更改,并从以下选项中选择适当的方法来修复诊断:
修复选项:
- 如果表达式不会导致行为更改:
- 如果
IntPtr
或UIntPtr
类型用作本机int
或uint
,则将类型更改为nint
或nuint
。 - 如果
IntPtr
或UIntPtr
类型用作本机指针,请将类型更改为相应的本机指针类型。 - 如果无法更改变量的类型,请禁止显示警告。
- 如果
- 如果表达式可能导致行为更改,请使用
checked
或unchecked
语句包装它以保留以前的行为。
示例
冲突:
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
类型用作本机int
或uint
,则将类型更改为nint
或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;
}
}
- 如果表达式可能导致行为更改,请使用
checked
或unchecked
语句包装它以保留以前的行为。
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);
}
}
何时禁止显示警告
如果表达式不会导致行为更改,可以安全地禁止显示此规则中的警告。