Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
CA1838: A P/Invokes paramétereinek elkerülése
| Tulajdonság | Érték |
|---|---|
| Szabályazonosító | CA1838 |
| Cím | A P/Invokes paramétereinek elkerülése StringBuilder |
| Kategória | Teljesítmény |
| A javítás romboló vagy nem romboló | Nem törhető |
| Alapértelmezés szerint engedélyezve a .NET 10-ben | Nem |
| Alkalmazandó nyelvek | C# és Visual Basic |
Ok
A P/Invoke paraméterrel StringBuilder rendelkezik.
Szabály leírása
A StringBuilder kiszolgáló vezérlés mindig létrehoz egy natív puffer másolatot, ami több foglalást eredményez egyetlen P/Invoke hívás esetén. A StringBuilder P/Invoke paraméterként való marsallásához a futtatási környezet a következő lépéseket hajtja végre:
- Foglaljon le egy natív puffert.
- Ha paraméterről van szó
In, másolja a tartalmát aStringBuildernatív pufferbe. - Ha paraméterről van szó
Out, másolja a natív puffert egy újonnan lefoglalt felügyelt tömbbe.
Alapértelmezés szerint StringBuilder, In és Out.
További információ a sztringek egyesítéséről (marshaling): Sztringek alapértelmezett marshal-olása.
Ez a szabály alapértelmezés szerint le van tiltva, mert eseti elemzést igényelhet arról, hogy a szabálysértés érdekes-e, és esetleg nem triviális újrabontást igényel a jogsértés kezeléséhez. A felhasználók a súlyosságuk konfigurálásával explicit módon engedélyezhetik ezt a szabályt.
Szabálysértések kijavítása
A szabálysértések kezelése általában magában foglalja a P/Invoke és a hívók átdolgozását, hogy a StringBuilder helyett puffert használjanak. A konkrétumok a P/Invoke használati eseteitől függenek.
Íme egy példa a natív függvény által kitöltendő kimeneti pufferek gyakori forgatókönyvére StringBuilder :
// 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();
}
Olyan esetekben, amikor a puffer kicsi, és unsafe a kód elfogadható, a stackalloc használatával lefoglalhatja a puffert a veremen:
[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);
}
}
Nagyobb pufferek esetén egy új tömb foglalható le pufferként:
[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);
}
Ha a P/Invoke gyakran nagyobb pufferekre van meghívva, ArrayPool<T> akkor a velük járó ismétlődő foglalások és memóriaterhelés elkerülése érdekében használható:
[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);
}
}
Ha a puffer mérete a futtatókörnyezetig nem ismert, előfordulhat, hogy a puffert a méret alapján másképpen kell létrehozni, hogy a nagyméretű pufferek ne legyenek kiosztva a következővel stackalloc: .
Az előző példák 2 bájt széles karaktereket (CharSet.Unicode) használnak. Ha a natív függvény 1 bájt karaktert használ (CharSet.Ansi), byte puffer helyett char puffer használható. Példa:
[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);
}
}
Ha a paramétert bemenetként is használják, a puffereket fel kell tölteni a sztringadatokkal bármely explicit módon hozzáadott null terminátorsal.
Mikor kell letiltani a figyelmeztetéseket?
Ha nem aggódik a StringBuilder átvitel teljesítményre gyakorolt hatása miatt, hagyja figyelmen kívül a szabály megsértését.
Figyelmeztetés mellőzése
Ha csak egyetlen szabálysértést szeretne letiltani, adjon hozzá előfeldolgozási irányelveket a forrásfájlhoz a szabály letiltásához és újbóli engedélyezéséhez.
#pragma warning disable CA1838
// The code that's violating the rule is on this line.
#pragma warning restore CA1838
Ha le szeretné tiltani egy fájl, mappa vagy projekt szabályát, állítsa annak súlyosságát none a konfigurációs fájlban.
[*.{cs,vb}]
dotnet_diagnostic.CA1838.severity = none
További információ: Kódelemzési figyelmeztetések letiltása.