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 9'da 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ğiniStringBuilder
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 StringBuilder
Out
ş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 StringBuilder
bir 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 stackalloc
bü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.Ansi
byte
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 StringBuilder
iş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 none
olarak ayarlayın.
[*.{cs,vb}]
dotnet_diagnostic.CA1838.severity = none
Daha fazla bilgi için bkz . Kod analizi uyarılarını gizleme.