Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
CA1838: Hindari parameter
| 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 10 | 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 dariStringBuilderke 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.