Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
CA1838: Vyhněte se
| 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 10 | 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
Inparametr, zkopírujte obsahStringBuildernativní vyrovnávací paměti. - Pokud se jedná o
Outparametr, 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 stackallocpamě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.