Megosztás a következőn keresztül:


CA1838: A P/Invokes paramétereinek elkerülése StringBuilder

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 kompatibilitástörő vagy nem törik Nem törés
Alapértelmezés szerint engedélyezve a .NET 8-ban Nem

Ok

A P/Invoke paraméterrel StringBuilder rendelkezik.

Szabály leírása

A StringBuilder rendezés mindig létrehoz egy natív pufferpéldányt, ami több foglalást eredményez egy P/Invoke híváshoz. P/Invoke paraméterként történő marsallásához StringBuilder a futtatókörnyezet a következő lesz:

  • Natív puffer lefoglalása.
  • Ha paraméterről van szó In , másolja a tartalmát a StringBuilder natí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 az és InOuta .

További információ a sztringek rendezéséről: Sztringek alapértelmezett rendezése.

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 újramunkálását, hogy puffert használjanak ahelyett StringBuilder. 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éldául:

[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?

Tiltsa le a szabály megsértését, ha nem aggódik a rendezés StringBuilderteljesítményre gyakorolt hatása miatt.

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.

Kapcsolódó információk