CA1838: Hindari parameter StringBuilder
untuk P/Invokes
Properti | Nilai |
---|---|
ID Aturan | CA1838 |
Judul | Hindari StringBuilder parameter untuk P/Invokes |
Golongan | Performa |
Perbaikan bersifat disruptif atau non-disruptif | Non-disruptif |
Diaktifkan secara default di .NET 9 | No |
Penyebab
P/Invoke mempunyai parameter StringBuilder.
Deskripsi aturan
Pengaturan StringBuilder
selalu membuat sebuah salinan buffer asli, menghasilkan beberapa alokasi untuk satu panggilan P/Invoke. Untuk mengatur StringBuilder
sebagai parameter P/Invoke, runtime akan:
- Mengalokasikan buffer asli.
- Jika itu adalah parameter
In
, salin konten dariStringBuilder
ke buffer asli. - Jika ini adalah parameter
Out
, salin buffer asli ke dalam larik terkelola yang baru dialokasikan.
Secara default, StringBuilder
adalah In
dan Out
.
Untuk informasi selengkapnya tentang pengaturan string, lihat Pengaturan default untuk string.
Aturan ini dinonaktifkan secara default, karena bisa memerlukan analisis kasus demi kasus apakah pelanggaran menarik serta berpotensi melakukan refaktor non-trivial untuk mengatasi pelanggaran. Pengguna bisa mengaktifkan aturan ini secara eksplisit dengan mengonfigurasi tingkat keparahannya.
Cara memperbaiki pelanggaran
Secara umum, mengatasi pelanggaran melibatkan pengerjaan ulang P/Invoke dan pemanggilnya untuk menggunakan buffer alih-alih StringBuilder
. Spesifikasinya akan tergantung pada kasus penggunaan untuk P/Invoke.
Berikut adalah contoh untuk skenario umum penggunaan StringBuilder
sebagai sebuah buffer output yang akan diisi oleh fungsi asli:
// 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();
}
Untuk kasus penggunaan di mana buffer kecil dan kode unsafe
dapat diterima, stackalloc bisa digunakan untuk mengalokasikan buffer pada tumpukan:
[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);
}
}
Untuk buffer yang lebih besar, array baru bisa dialokasikan sebagai buffer:
[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);
}
Ketika P/Invoke sering dipanggil untuk buffer yang lebih besar, ArrayPool<T> bisa digunakan untuk menghindari alokasi berulang serta tekanan memori yang disertakan dengannya:
[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);
}
}
Apabila ukuran buffer tidak diketahui sampai runtime, buffer mungkin perlu dibuat secara berbeda berdasarkan ukuran untuk menghindari alokasi buffer besar dengan stackalloc
.
Contoh sebelumnya menggunakan karakter lebar 2-byte (CharSet.Unicode
). Jika fungsi asli menggunakan karakter 1-byte (CharSet.Ansi
), buffer byte
bisa digunakan alih-alih buffer char
. Contohnya:
[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);
}
}
Jika parameter juga digunakan sebagai input, buffer perlu diisi dengan data string dengan terminator null yang ditambahkan secara eksplisit.
Kapan harus menekan peringatan
Tekan pelanggaran aturan ini jika Anda tidak khawatir tentang dampak performa dari pengaturan StringBuilder
.
Menyembunyikan peringatan
Jika Anda hanya ingin menyembunyikan satu pelanggaran, tambahkan arahan praprosedur ke file sumber Anda untuk dinonaktifkan lalu aktifkan kembali aturannya.
#pragma warning disable CA1838
// The code that's violating the rule is on this line.
#pragma warning restore CA1838
Untuk menonaktifkan aturan untuk file, folder, atau proyek, atur tingkat keparahannya ke none
dalam file konfigurasi.
[*.{cs,vb}]
dotnet_diagnostic.CA1838.severity = none
Untuk informasi selengkapnya, lihat Cara menyembunyikan peringatan analisis kode.