Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
| Proprietà | valore |
|---|---|
| ID regola | CA2020 |
| Title | Impedire la modifica comportamentale causata dagli operatori predefiniti di IntPtr/UIntPtr |
| Categoria | Affidabilità |
| La correzione causa un'interruzione o meno | Non rompente |
| Abilitato per impostazione predefinita in .NET 10 | Come suggerimento |
| Linguaggi applicabili | C# |
Causa
Questa regola viene attivata quando rileva una modifica comportamentale tra .NET 6 e .NET 7 introdotta dai nuovi operatori predefiniti di IntPtr e UIntPtr.
Descrizione regola
Con la funzionalità Numeric IntPtr e UIntPtr hanno acquisito operatori predefiniti per conversioni, operazioni unarie e operazioni binarie. Questi operatori potrebbero generare un'eccezione in caso di overflow in un contesto controllato, oppure potrebbero non generarla in un contesto non controllato, rispetto agli operatori personalizzati delle versioni precedenti a .NET 6. È possibile che si verifichi questa modifica comportamentale durante l'aggiornamento a .NET 7.
Elenco delle API interessate
| Operatore | Contesto | In .NET 7 | In .NET 6 e versioni precedenti | Esempio |
|---|---|---|---|---|
| operator +(IntPtr, int) | verificato | Genera quando si verifica un overflow | Non genera un'eccezione quando si verifica un overflow | checked(intPtrVariable + 2); |
| operator -(IntPtr, int) | controllato | Genera quando si verifica un overflow | Non genera un'eccezione quando si verifica un overflow | checked(intPtrVariable - 2); |
| Operatore explicit IntPtr(long) | non selezionato | Non genera un'eccezione quando si verifica un overflow | Può lanciare eccezioni in contesti a 32 bit | (IntPtr)longVariable; |
| Operatore explicit void*(IntPtr) | verificato | genera quando si verifica un overflow | Non genera un'eccezione quando si verifica un overflow | checked((void*)intPtrVariable); |
| Operatore explicit IntPtr(void*) | verificato | genera quando si verifica un overflow | Non genera eccezioni in caso di overflow | checked((IntPtr)voidPtrVariable); |
| operatore esplicito int(IntPtr) | non selezionato | Non genera un'eccezione quando si verifica un overflow | Può generare un'eccezione in contesti a 64 bit | (int)intPtrVariable; |
| operator +(UIntPtr, int) | verificato | Genera quando si verifica un overflow | Non genera un'eccezione quando si verifica un overflow | checked(uintPtrVariable + 2); |
| operator -(UIntPtr, int) | verificato | Genera quando si verifica un overflow | Non genera un'eccezione quando si verifica un overflow | checked(uintPtrVariable - 2); |
| Operatore esplicito UIntPtr(ulong) | non selezionato | Non genera un'eccezione quando si verifica un overflow | Può lanciare eccezioni in contesti a 32 bit | (UIntPtr)uLongVariable |
| Operatore esplicito uint(UIntPtr) | non selezionato | Non genera un'eccezione quando si verifica un overflow | Può generare un'eccezione nei contesti a 64 bit | (uint)uintPtrVariable |
Come correggere le violazioni
Esaminare il codice per determinare se l'espressione contrassegnata potrebbe causare una modifica comportamentale e scegliere un modo appropriato per correggere la diagnostica dalle opzioni seguenti:
Opzioni di correzione:
- Se l'espressione non provocherebbe una modifica comportamentale:
- Se il tipo
IntPtroUIntPtrè utilizzato comeintouintnativo, cambiare il tipo innintonuint. - Se il tipo
IntPtroUIntPtrviene utilizzato come puntatore nativo, modificare il tipo nel corrispondente tipo di puntatore nativo. - Se non è possibile modificare il tipo della variabile, eliminare l'avviso.
- Se il tipo
- Se l'espressione potrebbe causare una modifica comportamentale, eseguire il wrapping con un'istruzione
checkedouncheckedper mantenere il comportamento precedente.
Esempio
Violazione:
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.
}
}
Correzione:
- Se l'espressione non provoca una modifica comportamentale e
IntPtroUIntPtrtipo è usato come nativointouint, modificare il tipo innintonuint.
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;
}
}
- Se l'espressione potrebbe causare una modifica comportamentale, eseguire il wrapping con un'istruzione
checkedouncheckedper mantenere il comportamento precedente.
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);
}
}
Quando eliminare gli avvisi
Se l'espressione non provocherebbe una modifica comportamentale, è possibile eliminare un avviso da questa regola.