CA1838: P/Invokes parametrelerinden kaçının StringBuilder

Özellik Değer
Kural Kimliği CA1838
Başlık P/Invokes parametrelerinden kaçının StringBuilder
Kategori Performans
Hataya neden olan veya bozulmayan düzeltme Hataya neden olmayan
.NET 8'de varsayılan olarak etkin Hayır

Neden

P /Invoke parametresine sahiptir StringBuilder .

Kural açıklaması

öğesinin StringBuilder düzeni her zaman yerel bir arabellek kopyası oluşturur ve bu da bir P/Invoke çağrısı için birden çok ayırmaya neden olur. P/Invoke parametresi olarak sıralamak StringBuilder için çalışma zamanı şunları yapacaktır:

  • Yerel arabellek ayırma.
  • Bu bir In parametreyse, içeriğini StringBuilder yerel arabelleğe kopyalayın.
  • Bu bir Out parametreyse, yerel arabelleği yeni ayrılan yönetilen bir diziye kopyalayın.

Varsayılan olarak ve StringBuilderOutşeklindedirIn.

Dizeleri sıralama hakkında daha fazla bilgi için bkz . Dizeler için varsayılan sıralama.

Bu kural varsayılan olarak devre dışıdır, çünkü ihlalin ilgi çekici olup olmadığının büyük/küçük harf analizini ve ihlali ele almak için potansiyel olarak önemsiz olmayan yeniden düzenlemeyi gerektirebilir. Kullanıcılar önem derecesini yapılandırarak bu kuralı açıkça etkinleştirebilir.

İhlalleri düzeltme

Genel olarak, bir ihlalin ele alınması, P/Invoke ve çağıranlarının yerine StringBuilderbir arabellek kullanmak üzere yeniden çalışmasını içerir. Ayrıntılar, P/Invoke için kullanım örneklerine bağlıdır.

Burada, yerel işlev tarafından doldurulacak bir çıkış arabelleği olarak kullanmanın StringBuilder yaygın senaryosuna bir örnek verilmiştir:

// 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();
}

Arabelleğin küçük olduğu ve unsafe kodun kabul edilebilir olduğu kullanım örnekleri için stackalloc, yığının üzerindeki arabelleği ayırmak için kullanılabilir:

[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);
    }
}

Daha büyük arabellekler için arabellek olarak yeni bir dizi ayrılabilir:

[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);
}

P/Invoke daha büyük arabellekler için sık sık çağrıldığında, ArrayPool<T> bunlarla birlikte gelen yinelenen ayırmaları ve bellek baskısını önlemek için kullanılabilir:

[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);
    }
}

Arabellek boyutu çalışma zamanına kadar bilinmiyorsa, ile stackallocbüyük arabelleklerin ayırmasını önlemek için arabellek boyutuna göre farklı şekilde oluşturulması gerekebilir.

Yukarıdaki örneklerde 2 bayt genişliğinde karakterler (CharSet.Unicode kullanılır). Yerel işlev 1 bayt karakter ()CharSet.Ansibyte kullanıyorsa arabellek yerine arabellek char kullanılabilir. Örneğin:

[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);
    }
}

Parametre giriş olarak da kullanılıyorsa, arabelleklerin dize verileriyle açıkça eklenen herhangi bir null sonlandırıcıyla doldurulması gerekir.

Uyarıların ne zaman bastırılması gerekiyor?

Bir hazırlama StringBuilderişleminin performans etkisi konusunda endişeniz yoksa, bu kuralın ihlalini bastırın.

Uyarıyı gizleme

Yalnızca tek bir ihlali engellemek istiyorsanız, kuralı devre dışı bırakmak ve sonra yeniden etkinleştirmek için kaynak dosyanıza ön işlemci yönergeleri ekleyin.

#pragma warning disable CA1838
// The code that's violating the rule is on this line.
#pragma warning restore CA1838

Bir dosya, klasör veya projenin kuralını devre dışı bırakmak için, yapılandırma dosyasındaki önem derecesini noneolarak ayarlayın.

[*.{cs,vb}]
dotnet_diagnostic.CA1838.severity = none

Daha fazla bilgi için bkz . Kod analizi uyarılarını gizleme.

Ayrıca bkz.