CA1838: Parameters voor P/Invokes vermijden StringBuilder
Eigenschappen | Weergegeven als |
---|---|
Regel-id | CA1838 |
Titel | Parameters voor P/Invokes vermijden StringBuilder |
Categorie | Prestaties |
Oplossing is brekend of niet-brekend | Niet-brekend |
Standaard ingeschakeld in .NET 9 | Nee |
Oorzaak
Een P/Invoke heeft een StringBuilder parameter.
Beschrijving van regel
Marshalling van StringBuilder
maakt altijd een systeemeigen bufferkopie, wat resulteert in meerdere toewijzingen voor één P/Invoke-aanroep. De runtime doet het volgende om een marshal a StringBuilder
as a P/Invoke-parameter te gebruiken:
- Wijs een systeemeigen buffer toe.
- Als het een
In
parameter is, kopieert u de inhoud van deStringBuilder
naar de systeemeigen buffer. - Als het een
Out
parameter is, kopieert u de systeemeigen buffer naar een zojuist toegewezen beheerde matrix.
StringBuilder
Standaard is In
en Out
.
Zie Standaard marshalling voor tekenreeksen voor meer informatie over marshallreeksen.
Deze regel is standaard uitgeschakeld, omdat hiervoor een case-by-case-analyse kan worden vereist van het feit of de schending van belang is en mogelijk niet-triviale herstructurering om de schending aan te pakken. Gebruikers kunnen deze regel expliciet inschakelen door de ernst ervan te configureren.
Schendingen oplossen
Over het algemeen omvat het aanpakken van een schending het opnieuw bewerken van de P/Invoke en de bellers om een buffer te gebruiken in plaats van StringBuilder
. De details zijn afhankelijk van de gebruiksvoorbeelden voor de P/Invoke.
Hier volgt een voorbeeld voor het algemene scenario van het gebruik StringBuilder
als uitvoerbuffer die moet worden gevuld door de systeemeigen functie:
// 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();
}
Voor gebruiksscenario's waarbij de buffer klein is en unsafe
code acceptabel is, kan stackalloc worden gebruikt om de buffer toe te wijzen aan de 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);
}
}
Voor grotere buffers kan een nieuwe matrix als buffer worden toegewezen:
[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);
}
Wanneer de P/Invoke vaak wordt aangeroepen voor grotere buffers, ArrayPool<T> kan worden gebruikt om de herhaalde toewijzingen en geheugendruk te voorkomen die bij deze buffers hoort:
[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);
}
}
Als de buffergrootte pas bekend is tijdens de runtime, moet de buffer mogelijk anders worden gemaakt op basis van de grootte om te voorkomen dat grote buffers worden toe te wijzen.stackalloc
In de voorgaande voorbeelden worden 2 bytebrede tekens (CharSet.Unicode
) gebruikt. Als de systeemeigen functie 1-bytetekens (CharSet.Ansi
) gebruikt, kan een byte
buffer worden gebruikt in plaats van een char
buffer. Voorbeeld:
[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);
}
}
Als de parameter ook wordt gebruikt als invoer, moeten de buffers worden gevuld met de tekenreeksgegevens met een null-afsluiter die expliciet is toegevoegd.
Wanneer waarschuwingen onderdrukken
Onderdrukt een schending van deze regel als u zich geen zorgen maakt over de invloed op de prestaties van het marshallen van een StringBuilder
.
Een waarschuwing onderdrukken
Als u slechts één schending wilt onderdrukken, voegt u preprocessorrichtlijnen toe aan uw bronbestand om de regel uit te schakelen en vervolgens opnieuw in te schakelen.
#pragma warning disable CA1838
// The code that's violating the rule is on this line.
#pragma warning restore CA1838
Als u de regel voor een bestand, map of project wilt uitschakelen, stelt u de ernst none
ervan in op het configuratiebestand.
[*.{cs,vb}]
dotnet_diagnostic.CA1838.severity = none
Zie Codeanalysewaarschuwingen onderdrukken voor meer informatie.