다음을 통해 공유


CA2020: IntPtr/UIntPtr의 기본 제공 연산자로 인한 동작 변경 방지

속성
규칙 ID CA2020
제목 IntPtr/UIntPtr의 기본 제공 연산자로 인한 동작 변경 방지
범주 신뢰성
수정 사항이 호환성을 깨뜨리는지 여부 또는 무중단인지 여부 주요 변경 아님
.NET 10에서 기본적으로 사용하도록 설정 제안 사항
적용 가능한 언어 C#

원인

이 규칙은 IntPtrUIntPtr의 새 기본 제공 연산자가 도입한 .NET 6과 .NET 7 간의 동작 변경을 감지할 때 트리거됩니다.

규칙 설명

numeric IntPtr 기능으로 IntPtrUIntPtr은/는 변환, 단항 연산 및 이진 연산을 위한 기본 제공 연산자를 얻었습니다. 이러한 연산자는 선택된 컨텍스트 내에서 오버플로될 때 throw될 수 있으며 혹은 .NET 6 및 이전 버전의 이전 사용자 정의 연산자와 비교하여 선택되지 않은 컨텍스트에서 throw되지 않을 수 있습니다. .NET 7로 업그레이드할 때 이 동작이 변경될 수 있습니다.

영향을 받는 API의 목록

연산자 Context .NET 7 .NET 6 및 이전 버전
operator +(IntPtr, int) 체크됨 오버플로가 발생할 때 throw합니다. 오버플로 시 throw하지 않음 checked(intPtrVariable + 2);
operator -(IntPtr, int) 확인됨 오버플로 시 예외 발생 오버플로 시 throw하지 않음 checked(intPtrVariable - 2);
explicit 연산자 IntPtr(long) 체크되지 않음 오버플로 시 throw하지 않음 32비트 컨텍스트에서 throw할 수 있음 (IntPtr)longVariable;
explicit 연산자 void*(IntPtr) 검사함 오버플로 시 예외를 throw합니다. 오버플로가 발생해도 throw하지 않음 checked((void*)intPtrVariable);
explicit 연산자 IntPtr(void*) 검사됨 오버플로 시 예외를 발생시킴 오버플로 시 throw하지 않음 checked((IntPtr)voidPtrVariable);
explicit 연산자 int(IntPtr) 확인되지 않음 오버플로 시 throw하지 않음 64비트 컨텍스트에서 throw할 수 있음 (int)intPtrVariable;
operator +(UIntPtr, int) 확인됨 오버플로 발생 시 예외를 던집니다 오버플로 시 throw하지 않음 checked(uintPtrVariable + 2);
operator -(UIntPtr, int) 검사됨 오버플로 시 예외가 발생합니다. 오버플로 시 throw하지 않음 checked(uintPtrVariable - 2);
explicit 연산자 UIntPtr(ulong) 선택되지 않음 오버플로 시 throw하지 않음 32비트 컨텍스트에서 throw할 수 있음 (UIntPtr)uLongVariable
explicit 연산자 uint(UIntPtr) 체크되지 않음 오버플로 시 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 또는 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);
    }
}

경고를 표시하지 않는 경우

식이 동작 변경을 일으키지 않는 경우 이 규칙의 경고를 표시하지 않는 것이 안전합니다.

참고하기