Menyalin dan Menyematkan

Saat menyusun data, interop marshaller dapat menyalin atau menyematkan data yang sedang disusun. Menyalin data menempatkan salinan data dari satu lokasi memori di lokasi memori lain. Ilustrasi berikut memperlihatkan perbedaan antara menyalin tipe nilai dan menyalin tipe yang diteruskan dengan referensi dari memori terkelola ke tidak terkelola.

Diagram that shows how value and reference types are copied.

Argumen metode yang diteruskan oleh nilai disusun ke kode yang tidak dikelola sebagai nilai pada tumpukan. Proses penyalinan dilakukan secara langsung. Argumen yang diteruskan dengan referensi diteruskan sebagai pointer pada tumpukan. Jenis referensi juga diteruskan dengan nilai dan referensi. Seperti yang diperlihatkan ilustrasi berikut, tipe referensi yang diteruskan oleh nilai akan disalin atau disematkan:

Diagram showing reference types passed by value and by reference.

Menyematkan sementara mengunci data di lokasi memori saat ini, sehingga menjaganya agar tidak dipindahkan oleh pengumpul sampah runtime bahasa umum. Penyusun menyematkan data untuk mengurangi overhead penyalinan dan meningkatkan performa. Jenis data menentukan apakah itu disalin atau disematkan selama proses penyusunan. Penyematan secara otomatis dilakukan selama menyusun objek seperti String, namun Anda juga dapat menyematkan memori secara manual menggunakan kelas GCHandle.

Kelas Blittable yang Diformat

Kelas blittable yang diformat memiliki tata letak tetap (diformat) dan representasi data umum di memori terkelola dan tidak terkelola. Ketika tipe ini membutuhkan marshalling, pointer ke objek di heap diteruskan ke callee secara langsung. Penerima panggilan dapat mengubah konten lokasi memori yang dirujuk oleh penunjuk.

Catatan

Penerima panggilan dapat mengubah konten memori jika parameter ditandai Keluar atau Masuk/Keluar. Sebaliknya, penerima panggilan harus menghindari perubahan konten ketika parameter diatur ke marshal sebagai In, yang merupakan default untuk jenis blittable yang diformat. Memodifikasi objek In menghasilkan masalah ketika kelas yang sama diekspor ke pustaka jenis dan digunakan untuk melakukan panggilan lintas apartemen.

Kelas Blittable yang Diformat

Kelas non-blittable yang diformat memiliki tata letak tetap (diformat) tetapi representasi data berbeda dalam memori terkelola dan tidak terkelola. Data dapat memerlukan transformasi dalam kondisi berikut:

  • Jika kelas yang tidak dapat di-blittable dinamai berdasarkan nilai, penerima panggilan menerima penunjuk ke salinan struktur data.

  • Jika kelas yang tidak dapat di-blittable dinamai berdasarkan referensi, penerima panggilan menerima penunjuk ke penunjuk ke salinan struktur data.

  • Jika atribut InAttribute diatur, salinan ini selalu diinisialisasi dengan status instans, marshalling seperlunya.

  • Jika atribut OutAttribute diatur, status selalu disalin kembali ke instans saat dikembalikan, marshalling seperlunya.

  • Jika InAttribute dan OutAttribute diatur, kedua salinan diperlukan. Jika salah satu atribut dihilangkan, penyusun dapat mengoptimalkan dengan menghilangkan salah satu salinan.

Jenis Referensi

Jenis referensi dapat diteruskan berdasarkan nilai atau referensi. Ketika mereka diteruskan oleh nilai, penunjuk ke jenis diteruskan pada tumpukan. Saat diteruskan oleh referensi, penunjuk ke penunjuk ke jenis diteruskan pada tumpukan.

Jenis referensi memiliki perilaku bersyarat berikut:

  • Jika jenis referensi diteruskan berdasarkan nilai dan memiliki anggota jenis yang tidak dapat di-blittable, jenisnya dikonversi dua kali:

    • Ketika argumen diteruskan ke sisi yang tidak dikelola.

    • Saat kembali dari panggilan.

    Untuk menghindari penyalinan dan konversi yang tidak perlu, jenis ini disusun sebagai Dalam parameter. Anda harus secara eksplisit menerapkan atribut InAttribute dan OutAttribute ke argumen pemanggil untuk melihat perubahan yang dibuat oleh penerima panggilan.

  • Jika jenis referensi diteruskan oleh nilai dan hanya memiliki anggota jenis yang dapat di-blittable, jenis tersebut dapat disematkan selama marshalling dan setiap perubahan yang dilakukan pada anggota jenis oleh penerima panggilan terlihat oleh pemanggil. Terapkan InAttribute dan OutAttribute secara eksplisit jika Anda menginginkan perilaku ini. Tanpa atribut arah ini, marshaller interop tidak mengekspor informasi arah ke pustaka jenis (diekspor sebagai In, yang merupakan default) dan ini dapat menyebabkan masalah dengan marshalling lintas apartemen COM.

  • Jika jenis referensi diteruskan oleh referensi, itu akan dinamai sebagai Masuk/Keluar secara default.

System.String dan System.Text.StringBuilder

Ketika data disusun ke kode yang tidak dikelola berdasarkan nilai atau dengan referensi, penyusun biasanya menyalin data ke buffer sekunder (mungkin mengonversi kumpulan karakter selama salinan) dan meneruskan referensi ke buffer kepada penerima panggilan. Kecuali referensinya adalah BSTR yang dialokasikan dengan SysAllocString, referensi selalu dialokasikan dengan CoTaskMemAlloc.

Sebagai pengoptimalan saat String atau StringBuilder disusun berdasarkan nilai (seperti string karakter Unicode), marshaller meneruskan pointer langsung ke callee ke string terkelola di buffer Unicode internal daripada menyalinnya ke buffer baru.

Perhatian

Ketika string diteruskan oleh nilai, penerima panggilan tidak boleh mengubah referensi yang diteruskan oleh marshaller. Melakukannya dapat merusak tumpukan terkelola.

Ketika System.String dilewatkan dengan referensi, penyusun menyalin isi string ke buffer sekunder sebelum melakukan panggilan. Kemudian menyalin konten buffer ke dalam string baru saat dikembalikan dari panggilan. Teknik ini memastikan bahwa string terkelola yang tidak dapat diubah tetap tidak berubah.

Saat System.Text.StringBuilder diteruskan dengan nilai, penyusun meneruskan referensi ke salinan sementara buffer internal StringBuilder ke pemanggil. Pemanggil dan penerima panggilan harus menyetujui ukuran buffer. Pemanggil bertanggung jawab untuk membuat StringBuilder dengan panjang yang memadai. Penerima panggilan harus mengambil tindakan pencegahan yang diperlukan untuk memastikan bahwa buffer tidak diserbu. StringBuilder adalah pengecualian untuk aturan bahwa jenis referensi yang diteruskan oleh nilai diteruskan sebagai In parameter secara default. StringBuilder selalu diteruskan sebagai In/Out.

Lihat juga