CA2020: IntPtr/UIntPtr의 기본 제공 연산자로 인한 동작 변경 방지
속성 | 값 |
---|---|
규칙 ID | CA2020 |
제목 | IntPtr/UIntPtr의 기본 제공 연산자로 인한 동작 변경 방지 |
범주 | 신뢰성 |
수정 사항이 주요 변경인지 여부 | 주요 변경 아님 |
.NET 9에서 기본적으로 사용 | 제안 사항 |
원인
이 규칙은 IntPtr 및 UIntPtr의 새 기본 제공 연산자가 도입한 .NET 6과 .NET 7 간의 동작 변경을 감지할 때 발생합니다.
규칙 설명
IntPtr과 UIntPtr은 숫자 IntPtr 기능을 사용하여 변환, 단항 연산 및 이진 연산에 대한 기본 제공 연산자를 얻었습니다. 이러한 연산자는 선택된 컨텍스트 내에서 오버플로될 때 throw될 수 있으며 혹은 .NET 6 및 이전 버전의 이전 사용자 정의 연산자와 비교하여 선택되지 않은 컨텍스트에서 throw되지 않을 수 있습니다. .NET 7로 업그레이드할 때 이 동작이 변경될 수 있습니다.
영향을 받는 API의 목록
연산자 | Context | .NET 7 | .NET 6 및 이전 버전 | 예 |
---|---|---|---|---|
operator +(IntPtr, int) | checked | 오버플로 시 throw | 오버플로 시 throw하지 않음 | checked(intPtrVariable + 2); |
operator -(IntPtr, int) | checked | 오버플로 시 throw | 오버플로 시 throw하지 않음 | checked(intPtrVariable - 2); |
explicit operator IntPtr(long) | unchecked | 오버플로 시 throw하지 않음 | 32비트 컨텍스트에서 throw할 수 있음 | (IntPtr)longVariable; |
explicit operator void*(IntPtr) | checked | 오버플로 시 throw | 오버플로 시 throw하지 않음 | checked((void*)intPtrVariable); |
explicit operator IntPtr(void*) | checked | 오버플로 시 throw | 오버플로 시 throw하지 않음 | checked((IntPtr)voidPtrVariable); |
explicit operator int(IntPtr) | unchecked | 오버플로 시 throw하지 않음 | 64비트 컨텍스트에서 throw할 수 있음 | (int)intPtrVariable; |
operator +(UIntPtr, int) | checked | 오버플로 시 throw | 오버플로 시 throw하지 않음 | checked(uintPtrVariable + 2); |
operator -(UIntPtr, int) | checked | 오버플로 시 throw | 오버플로 시 throw하지 않음 | checked(uintPtrVariable - 2); |
explicit operator UIntPtr(ulong) | unchecked | 오버플로 시 throw하지 않음 | 32비트 컨텍스트에서 throw할 수 있음 | (UIntPtr)uLongVariable |
explicit operator uint(UIntPtr) | unchecked | 오버플로 시 throw하지 않음 | 64비트 컨텍스트에서 throw할 수 있음 | (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.
}
}
Fix:
- 식이 동작 변경을 일으키지 않고 또는 형식이
IntPtr
네이티브int
로 사용되는 경우 또는uint
형식을 로nint
변경합니다nuint
.UIntPtr
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);
}
}
경고를 표시하지 않는 경우
식이 동작 변경을 일으키지 않는 경우 이 규칙의 경고를 표시하지 않는 것이 안전합니다.
참고 항목
GitHub에서 Microsoft와 공동 작업
이 콘텐츠의 원본은 GitHub에서 찾을 수 있으며, 여기서 문제와 끌어오기 요청을 만들고 검토할 수도 있습니다. 자세한 내용은 참여자 가이드를 참조하세요.
.NET