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.
CA1838: Evitare
| Proprietà | valore |
|---|---|
| ID regola | CA1838 |
| Title | Evitare StringBuilder parametri per P/Invoke |
| Categoria | Prestazioni |
| La correzione causa un'interruzione o meno | Non causa un'interruzione |
| Abilitato per impostazione predefinita in .NET 10 | No |
Causa
Un P/Invoke ha un StringBuilder parametro .
Descrizione regola
Il marshalling di StringBuilder crea sempre una copia del buffer nativa, generando più allocazioni per una chiamata P/Invoke. Per effettuare il marshalling di un come StringBuilder parametro P/Invoke, il runtime eseguirà le seguenti attività:
- Allocare un buffer nativo.
- Se si tratta di un
Inparametro, copiare il contenuto di nelStringBuilderbuffer nativo. - Se si tratta di un
Outparametro, copiare il buffer nativo in una matrice gestita appena allocata.
Per impostazione predefinita, StringBuilder è In e Out.
Per altre informazioni sul marshalling delle stringhe, vedere Marshalling predefinito per le stringhe.
Questa regola è disabilitata per impostazione predefinita, perché può richiedere l'analisi caso per caso se la violazione è di interesse e il refactoring potenzialmente non semplice per risolvere la violazione. Gli utenti possono abilitare questa regola in modo esplicito configurandone la gravità.
Come correggere le violazioni
In generale, la risoluzione di una violazione comporta la rielaborazione di P/Invoke e dei relativi chiamanti per l'uso di un buffer anziché di StringBuilder. Le specifiche dipendono dai casi d'uso per P/Invoke.
Di seguito è riportato un esempio per lo scenario comune di utilizzo StringBuilder di come buffer di output da riempire tramite la funzione nativa:
// Violation
[DllImport("MyLibrary", CharSet = CharSet.Unicode)]
private static extern void Foo(StringBuilder sb, ref int length);
public void Bar()
{
int BufferSize = ...
StringBuilder sb = new StringBuilder(BufferSize);
int len = sb.Capacity;
Foo(sb, ref len);
string result = sb.ToString();
}
Per i casi d'uso in cui il buffer è piccolo e unsafe il codice è accettabile, stackalloc può essere usato per allocare il buffer nello stack:
[DllImport("MyLibrary", CharSet = CharSet.Unicode)]
private static extern unsafe void Foo(char* buffer, ref int length);
public void Bar()
{
int BufferSize = ...
unsafe
{
char* buffer = stackalloc char[BufferSize];
int len = BufferSize;
Foo(buffer, ref len);
string result = new string(buffer);
}
}
Per i buffer di dimensioni maggiori, è possibile allocare una nuova matrice come buffer:
[DllImport("MyLibrary", CharSet = CharSet.Unicode)]
private static extern void Foo([Out] char[] buffer, ref int length);
public void Bar()
{
int BufferSize = ...
char[] buffer = new char[BufferSize];
int len = buffer.Length;
Foo(buffer, ref len);
string result = new string(buffer);
}
Quando il P/Invoke viene spesso chiamato per buffer di dimensioni maggiori, ArrayPool<T> può essere usato per evitare le allocazioni ripetute e la pressione di memoria fornita con essi:
[DllImport("MyLibrary", CharSet = CharSet.Unicode)]
private static extern unsafe void Foo([Out] char[] buffer, ref int length);
public void Bar()
{
int BufferSize = ...
char[] buffer = ArrayPool<char>.Shared.Rent(BufferSize);
try
{
int len = buffer.Length;
Foo(buffer, ref len);
string result = new string(buffer);
}
finally
{
ArrayPool<char>.Shared.Return(buffer);
}
}
Se la dimensione del buffer non è nota fino al runtime, potrebbe essere necessario creare il buffer in modo diverso in base alle dimensioni per evitare di allocare buffer di grandi dimensioni con stackalloc.
Negli esempi precedenti vengono usati caratteri wide a 2 byte (CharSet.Unicode). Se la funzione nativa usa caratteri a 1 byte (CharSet.Ansi), è possibile usare un byte buffer anziché un char buffer. Ad esempio:
[DllImport("MyLibrary", CharSet = CharSet.Ansi)]
private static extern unsafe void Foo(byte* buffer, ref int length);
public void Bar()
{
int BufferSize = ...
unsafe
{
byte* buffer = stackalloc byte[BufferSize];
int len = BufferSize;
Foo(buffer, ref len);
string result = Marshal.PtrToStringAnsi((IntPtr)buffer);
}
}
Se il parametro viene usato anche come input, i buffer devono essere popolati con i dati stringa con qualsiasi carattere di terminazione Null aggiunto in modo esplicito.
Quando eliminare gli avvisi
Eliminare una violazione di questa regola se non si è interessati all'impatto sulle prestazioni del marshalling di un oggetto StringBuilder.
Eliminare un avviso
Se si vuole eliminare una singola violazione, aggiungere direttive del preprocessore al file di origine per disabilitare e quindi riabilitare la regola.
#pragma warning disable CA1838
// The code that's violating the rule is on this line.
#pragma warning restore CA1838
Per disabilitare la regola per un file, una cartella o un progetto, impostarne la gravità none su nel file di configurazione.
[*.{cs,vb}]
dotnet_diagnostic.CA1838.severity = none
Per altre informazioni, vedere Come eliminare gli avvisi di analisi del codice.