CA2020: Zapobieganie zmianom behawioralnym spowodowanym przez wbudowane operatory intPtr/UIntPtr
Właściwości | Wartość |
---|---|
Identyfikator reguły | CA2020 |
Tytuł | Zapobieganie zmianom zachowania spowodowanym przez wbudowane operatory intPtr/UIntPtr |
Kategoria | Niezawodność |
Poprawka powodująca niezgodność lub niezgodność | Niezgodność |
Domyślnie włączone na platformie .NET 9 | Jako sugestia |
Przyczyna
Ta reguła jest uruchamiana, gdy wykrywa zmianę zachowania między platformami .NET 6 i .NET 7 wprowadzonymi przez nowe wbudowane operatory systemów IntPtr i UIntPtr.
Opis reguły
Dzięki funkcjiIntPtr liczbowej IntPtr i UIntPtr zyskaliśmy wbudowane operatory konwersji, operacji jednoargumentowych i operacji binarnych. Te operatory mogą zgłaszać przepełnienie w ramach zaznaczonego kontekstu lub nie mogą zgłaszać niezaznaczonego kontekstu w porównaniu z poprzednimi operatorami zdefiniowanymi przez użytkownika na platformie .NET 6 i starszych wersjach. Ta zmiana zachowania może wystąpić podczas uaktualniania do platformy .NET 7.
Lista interfejsów API, których dotyczy problem
Operator | Kontekst | Na platformie .NET 7 | W programie .NET 6 i starszych wersjach | Przykład |
---|---|---|---|---|
operator +(IntPtr, int) | checked | Zgłasza przy przepełnieniu | Nie zgłasza się, gdy przepełnienie | checked(intPtrVariable + 2); |
operator -(IntPtr, int) | checked | Zgłasza przy przepełnieniu | Nie zgłasza się, gdy przepełnienie | checked(intPtrVariable - 2); |
jawny operator IntPtr(long) | unchecked | Nie zgłasza się, gdy przepełnienie | Może zgłaszać w kontekstach 32-bitowych | (IntPtr)longVariable; |
jawny operator void*(IntPtr) | checked | zgłaszane w przypadku przepełnienia | Nie zgłasza się, gdy przepełnienie | checked((void*)intPtrVariable); |
jawny operator IntPtr(void*) | checked | zgłaszane w przypadku przepełnienia | Nie zgłasza się, gdy przepełnienie | checked((IntPtr)voidPtrVariable); |
jawny operator int(IntPtr) | unchecked | Nie zgłasza się, gdy przepełnienie | Może zgłaszać w kontekstach 64-bitowych | (int)intPtrVariable; |
operator +(UIntPtr, int) | checked | Zgłasza przy przepełnieniu | Nie zgłasza się, gdy przepełnienie | checked(uintPtrVariable + 2); |
operator -(UIntPtr, int) | checked | Zgłasza przy przepełnieniu | Nie zgłasza się, gdy przepełnienie | checked(uintPtrVariable - 2); |
jawny operator UIntPtr(ulong) | unchecked | Nie zgłasza się, gdy przepełnienie | Może zgłaszać w kontekstach 32-bitowych | (UIntPtr)uLongVariable |
jawny operator uint(UIntPtr) | unchecked | Nie zgłasza się, gdy przepełnienie | Może zgłaszać w kontekstach 64-bitowych | (uint)uintPtrVariable |
Jak naprawić naruszenia
Sprawdź swój kod, aby ustalić, czy oflagowane wyrażenie może spowodować zmianę zachowania, i wybierz odpowiedni sposób naprawy diagnostyki z następujących opcji:
Opcje naprawy:
- Jeśli wyrażenie nie spowoduje zmiany behawioralnej:
IntPtr
Jeśli typ lubUIntPtr
jest używany jako natywnyint
lubuint
, zmień typ nanint
lubnuint
.IntPtr
Jeśli typ lubUIntPtr
jest używany jako wskaźnik macierzysty, zmień typ na odpowiedni natywny typ wskaźnika.- Jeśli nie możesz zmienić typu zmiennej, pomiń ostrzeżenie.
- Jeśli wyrażenie może spowodować zmianę zachowania, opakuj je za pomocą
checked
instrukcji lubunchecked
, aby zachować poprzednie zachowanie.
Przykład
Naruszenie:
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.
}
}
Poprawka:
- Jeśli wyrażenie nie spowoduje zmiany zachowania, a
IntPtr
typ lubUIntPtr
jest używany jako natywnyint
lubuint
, zmień typ nanint
lubnuint
.
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;
}
}
- Jeśli wyrażenie może spowodować zmianę zachowania, opakuj je za pomocą
checked
instrukcji lubunchecked
, aby zachować poprzednie zachowanie.
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);
}
}
Kiedy pomijać ostrzeżenia
Jeśli wyrażenie nie spowoduje zmiany behawioralnej, można bezpiecznie pominąć ostrzeżenie z tej reguły.