Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
CA1838: P/Invokes parametrelerinden kaçının
| Özellik | Değer |
|---|---|
| Kural Kimliği | CA1838 |
| Başlık | P/Invoke'ler için StringBuilder parametrelerinden kaçının |
| Kategori | Performans |
| Düzeltme bozucu ya da bozmayan olabilir | Kesintisiz |
| .NET 10'da varsayılan olarak etkin | Hayır |
| Geçerli diller | C# ve Visual Basic |
Neden
P /Invoke bir StringBuilder parametresine sahiptir.
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.
StringBuilder bir P/Invoke parametresi olarak düzenlemek için, çalışma zamanı şunları yapacaktır:
- Yerel bir arabellek ayırın.
- Eğer bu bir
Inparametreyse,StringBuilderiçeriğini yerel arabelleğe kopyalayın. - Bu bir
Outparametreyse, yerel arabelleği yeni tahsis edilen yönetilen bir diziye kopyalayın.
Varsayılan olarak, StringBuilderIn ve Out şeklindedir.
Dizeleri işlemeye hazırlama hakkında daha fazla bilgi için bkz. Dizeler için varsayılan işlemeye hazırlama.
Bu kural varsayılan olarak devre dışıdır, çünkü ihlalin ilgi çekici olup olmadığının duruma göre analizini ve ihlali ele almak için potansiyel olarak basit olmayan yeniden düzenlemeleri 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 StringBuilder yerine bir arabellek kullanacak şekilde yeniden düzenlenmesini 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 ve unsafe kodun kabul edilebilir olduğu kullanım senaryoları için, yığındaki tamponu ayırmak amacıyla stackalloc 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, yeni bir dizi arabellek olarak tahsis edilebilir.
[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, tekrar eden bellek ayırmalarını ve bu ayırmaların neden olduğu bellek baskısını önlemek için ArrayPool<T> 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, büyük arabelleklerin stackalloc ile ayrılması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 karakterler (CharSet.Ansi) kullanıyorsa, bir byte arabelleği, char arabelleği yerine 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.