다음을 통해 공유


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

속성
규칙 ID CA2020
제목 IntPtr/UIntPtr의 기본 제공 연산자로 인한 동작 변경 방지
범주 신뢰성
수정 사항이 주요 변경인지 여부 주요 변경 아님
.NET 9에서 기본적으로 사용 제안 사항

원인

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

규칙 설명

IntPtrUIntPtr숫자 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);
    }
}

경고를 표시하지 않는 경우

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

참고 항목