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 penggunaan StringBuilder parameter untuk P/Invokes |
| Kategori | Performa |
| Perbaikan bersifat merusak atau tidak merusak | Tidak terputus |
| Diaktifkan secara default di .NET 10 | Tidak |
| Bahasa yang berlaku | C# dan Visual Basic |
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 bawaan.
- Jika itu adalah parameter
In, salin konten dariStringBuilderke buffer asli. - Jika ini adalah parameter
Out, salin buffer asli ke dalam array terkelola yang baru dialokasikan.
Secara default, StringBuilder adalah In dan Out.
Untuk informasi selengkapnya tentang pemonasian string, lihat Pemonasian default untuk string.
Aturan ini dinonaktifkan secara default, karena bisa memerlukan analisis kasus per kasus untuk menentukan apakah pelanggaran tersebut relevan serta berpotensi memerlukan refaktor yang tidak sepele untuk mengatasinya. 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 dapat dialokasikan sebagai penyangga:
[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
Hapus pelanggaran aturan ini jika Anda tidak khawatir tentang dampak performa dari pemanggilan 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.