CA2020: предотвращение изменения поведения, вызванного встроенными операторами IntPtr/UIntPtr
Свойство | Значение |
---|---|
Идентификатор правила | CA2020 |
Заголовок | Предотвращение изменения поведения, вызванного встроенными операторами IntPtr/UIntPtr |
Категория | Надежность |
Исправление является критическим или не критическим | Не критическое |
Включен по умолчанию в .NET 9 | Как предложение |
Причина
Это правило возникает при обнаружении изменения поведения между .NET 6 и .NET 7, представленными новыми встроенными операторами IntPtr и UIntPtr.
Описание правила
Благодаря числовым функциямIntPtr IntPtr и UIntPtr встроенным операторам для преобразований, унарных операций и двоичных операций. Эти операторы могут вызываться при переполнении в проверяемом контексте или не могут вызываться в незаверченном контексте по сравнению с предыдущими пользовательскими операторами в .NET 6 и более ранних версиях. Это изменение поведения может возникнуть при обновлении до .NET 7.
Список затронутых API
Оператор | Контекст | В .NET 7 | В .NET 6 и более ранних версиях | Пример |
---|---|---|---|---|
оператор +(IntPtr, int) | включен | Вызывается при переполнении | Не вызывается при переполнении | checked(intPtrVariable + 2); |
оператор -(IntPtr, int) | включен | Вызывается при переполнении | Не вызывается при переполнении | checked(intPtrVariable - 2); |
явный оператор IntPtr(long) | не включен | Не вызывается при переполнении | Может вызываться в 32-разрядных контекстах | (IntPtr)longVariable; |
явный оператор void*(IntPtr) | включен | вызывается при переполнении | Не вызывается при переполнении | checked((void*)intPtrVariable); |
явный оператор IntPtr(void*) | включен | вызывается при переполнении | Не вызывается при переполнении | checked((IntPtr)voidPtrVariable); |
явный оператор int(IntPtr) | не включен | Не вызывается при переполнении | Может вызываться в 64-разрядных контекстах | (int)intPtrVariable; |
оператор +(UIntPtr, int) | включен | Вызывается при переполнении | Не вызывается при переполнении | checked(uintPtrVariable + 2); |
оператор -(UIntPtr, int) | включен | Вызывается при переполнении | Не вызывается при переполнении | checked(uintPtrVariable - 2); |
явный оператор UIntPtr(ulong) | не включен | Не вызывается при переполнении | Может вызываться в 32-разрядных контекстах | (UIntPtr)uLongVariable |
явный оператор uint(UIntPtr) | не включен | Не вызывается при переполнении | Может вызываться в 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);
}
}
Когда лучше отключить предупреждения
Если выражение не приведет к изменению поведения, это безопасно для подавления предупреждения из этого правила.