CA1838: Vyhněte se StringBuilder
parametrům volání nespravovaného kódu
Vlastnost | Hodnota |
---|---|
ID pravidla | CA1838 |
Název | Vyhněte se StringBuilder parametrům pro volání nespravovaného kódu |
Kategorie | Výkon |
Oprava způsobující chybu nebo chybu způsobující chybu | Nenarušující |
Povoleno ve výchozím nastavení v .NET 8 | No |
Příčina
Volání nespravovaného kódu má StringBuilder parametr.
Popis pravidla
Přiřazování StringBuilder
vždy vytvoří nativní kopii vyrovnávací paměti, což vede k několika přidělením pro jedno volání volání volání P/Invoke. Pokud chcete zařadit StringBuilder
parametr P/Invoke jako parametr, modul runtime:
- Přidělení nativní vyrovnávací paměti
- Pokud se jedná o
In
parametr, zkopírujte obsahStringBuilder
nativní vyrovnávací paměti. - Pokud se jedná o
Out
parametr, zkopírujte nativní vyrovnávací paměť do nově přiděleného spravovaného pole.
Ve výchozím nastavení StringBuilder
je In
a Out
.
Další informace o zařazování řetězců naleznete v tématu Výchozí zařazování řetězců.
Toto pravidlo je ve výchozím nastavení zakázané, protože může vyžadovat případnou analýzu toho, jestli je porušení zásad zájmu, a potenciálně ne triviální refaktoring k vyřešení porušení předpisů. Uživatelé můžou toto pravidlo explicitně povolit konfigurací závažnosti.
Jak opravit porušení
Obecně platí, že řešení porušení zahrnuje přepracování volání nespravovaného kódu a jeho volajících, aby místo toho používali vyrovnávací paměť StringBuilder
. Specifika by závisela na případech použití volání nespravovaného kódu.
Tady je příklad pro běžný scénář použití StringBuilder
jako výstupní vyrovnávací paměti, kterou má vyplnit nativní funkce:
// 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();
}
Pro případy použití, kdy je vyrovnávací paměť malá a unsafe
kód je přijatelný, lze stackalloc použít k přidělení vyrovnávací paměti v zásobníku:
[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);
}
}
U větších vyrovnávacích pamětí je možné přidělit nové pole jako vyrovnávací paměť:
[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);
}
Pokud se volání nespravovaného kódu často volá pro větší vyrovnávací paměti, ArrayPool<T> můžete se vyhnout opakovaným přidělením a zatížení paměti, které s nimi přichází:
[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);
}
}
Pokud velikost vyrovnávací paměti není známa do doby běhu, může být potřeba vytvořit odlišně podle velikosti, aby nedošlo k přidělování velkých vyrovnávacích stackalloc
pamětí .
V předchozích příkladech se používají 2 bajtové široké znaky (CharSet.Unicode
). Pokud nativní funkce používá 1 bajtové znaky (CharSet.Ansi
), byte
lze místo vyrovnávací paměti použít char
vyrovnávací paměť. Příklad:
[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);
}
}
Pokud se parametr používá také jako vstup, musí být vyrovnávací paměti vyplněny řetězcovými daty s libovolným ukončovacím znakem null explicitně přidaným.
Kdy potlačit upozornění
Potlačit porušení tohoto pravidla, pokud vás nezajímá dopad na výkon při zařazování StringBuilder
.
Potlačení upozornění
Pokud chcete pouze potlačit jedno porušení, přidejte do zdrojového souboru direktivy preprocesoru, abyste pravidlo zakázali a znovu povolili.
#pragma warning disable CA1838
// The code that's violating the rule is on this line.
#pragma warning restore CA1838
Pokud chcete pravidlo pro soubor, složku nebo projekt zakázat, nastavte jeho závažnost v none
konfiguračním souboru.
[*.{cs,vb}]
dotnet_diagnostic.CA1838.severity = none
Další informace naleznete v tématu Jak potlačit upozornění analýzy kódu.