boxing adalah proses membungkus nilai bertipe nilai ke dalam sebuah object di heap, sedangkan unboxing adalah proses sebaliknya yang mengeluarkan kembali nilai tersebut dari pembungkusnya. Keduanya menimbulkan biaya, karena setiap boxing mengalokasikan object baru di heap, dan jumlah kecil yang terjadi berulang kali membebani pengelolaan memori. Topik ini membahas kapan boxing terjadi, aturan unboxing yang bersifat ketat, serta dampak keduanya pada performa program.
Kapan boxing terjadi#
Boxing terjadi setiap kali nilai bertipe nilai dikonversi menjadi object atau menjadi antarmuka (interface) yang diimplementasikannya. Konversi ini dilakukan secara diam-diam oleh kompilator (compiler) karena selalu aman, sedangkan sebaliknya memerlukan konversi eksplisit. Program berikut memperlihatkan tiga kejadian sekaligus, yaitu penugasan int ke variabel object, kegagalan unboxing ke tipe yang salah, dan boxing yang menyusup saat nilai int dipakai melalui antarmuka IComparable.
Sifat diam-diam inilah yang membuat boxing mudah terlewat. Tidak ada kata kunci khusus yang menandainya, dan penugasan yang tampak sepele seperti memasukkan angka ke variabel object sudah memicu alokasi di heap. Petunjuk yang paling mudah dikenali adalah variabel bertipe object atau antarmuka yang menerima nilai bertipe nilai, termasuk elemen koleksi bertipe object dan parameter metode yang tidak generik. Apabila pemakaian pola itu berada di dalam perulangan, boxing terulang pada setiap iterasinya tanpa terlihat dari bentuk kodenya.
// Program demonstrasi boxing dan unboxing
int angka = 42;
object terboxing = angka; // boxing: nilai int dibungkus menjadi object di heap
Console.WriteLine($"terboxing.GetType() = {terboxing.GetType()}");
Console.WriteLine($"terboxing.GetType().IsValueType = {terboxing.GetType().IsValueType}");
int kembali = (int)terboxing; // unboxing: konversi eksplisit kembali ke int
Console.WriteLine($"kembali = {kembali}");
object kotak = 42;
try
{
double salah = (double)kotak; // unboxing wajib ke tipe yang persis sama
}
catch (InvalidCastException)
{
Console.WriteLine("Unboxing (double) kotak gagal: InvalidCastException");
}
IComparable pembanding = angka; // boxing juga terjadi saat int dikonversi ke interface
Console.WriteLine($"pembanding.CompareTo(10) = {pembanding.CompareTo(10)}");terboxing.GetType() = System.Int32
terboxing.GetType().IsValueType = True
kembali = 42
Unboxing (double) kotak gagal: InvalidCastException
pembanding.CompareTo(10) = 1Untuk menjalankan program tersebut dari folder topik, gunakan perintah berikut.
# aktifkan .NET yang terpasang di sandbox
export DOTNET_ROOT=/home/kal/.dotnet PATH=/home/kal/.dotnet:$PATH DOTNET_CLI_TELEMETRY_OPTOUT=1 DOTNET_NOLOGO=1
# jalankan program contoh dari folder topik
dotnet run --file Program.csterboxing.GetType() = System.Int32
terboxing.GetType().IsValueType = True
kembali = 42
Unboxing (double) kotak gagal: InvalidCastException
pembanding.CompareTo(10) = 1Keluaran mengonfirmasi bahwa pembungkusnya memang object berisi nilai int. Pemanggilan GetType() pada variabel terboxing mengembalikan System.Int32 dengan IsValueType bernilai True, yang berarti boxing tidak mengubah nilai menjadi tipe kelas biasa, melainkan menyalin nilai int ke dalam kotak System.Int32 di heap. Variabel aslinya tetap tidak tersentuh, dan unboxing dengan (int) menyalin kembali isinya ke variabel kembali.
Aturan unboxing#
Unboxing dilakukan dengan pengecoran eksplisit dan tipe sasarnanya wajib persis sama dengan tipe yang dibungkus. Program di atas menunjukkan pelanggarannya, yaitu kotak berisi int yang dicoba dikeluarkan sebagai double, dan proses tersebut dihentikan dengan exception InvalidCastException meskipun konversi dari int ke double di tempat lain dianggap wajar. Aturan yang ketat ini terjadi karena unboxing bekerja pada tingkat tata letak memori, bukan pada makna angkanya, sehingga tipe sasaran harus cocok terlebih dahulu sebelum konversi angka biasa dijalankan.
Pola yang aman untuk menghindari kegagalan tersebut adalah memeriksa tipe sebelum mengeluarkan isinya, misalnya dengan pola tipe if (kotak is int nilai), atau dengan operator as pada pemakaian antarmuka. Kedua cara itu mengubah kegagalan yang memicu exception menjadi pemeriksaan yang hasilnya dapat dicabangkan. Setelah nilai berhasil dikeluarkan, nilainya adalah salinan yang berdiri sendiri, sehingga mengubah kembali tidak pernah memengaruhi isi kotaknya.
Aturan yang persis itu memiliki satu pengecualian yang memang dirancang, yaitu pengecoran ke tipe nullable. Kotak berisi int boleh dikeluarkan sebagai int?, karena tipe nullable adalah pembungkus dari tipe dasarnya sendiri, sehingga pengecoran (int?)kotak tetap berhasil dan menghasilkan 42. Selain pengecualian tersebut, aturannya tidak menawarkan kelonggaran, sehingga tipe sasarannya perlu dicocokkan dengan tipe yang dibungkus sejak awal.
Dampak pada performa#
Setiap boxing mengalokasikan satu object baru di heap, dan alokasi itulah sumber biayanya. Object pembungkus memerlukan ruang untuk kepala object beserta nilainya, dan pada runtime 64-bit pembungkus satu nilai int berukuran 24 byte. Sebagai gambaran kasar dari pengukuran sekali jalan pada saat modul ini ditulis, satu juta operasi boxing beserta array penampungnya mengalokasikan sekitar 32 MB, dengan angka yang bergantung pada mesin dan versi runtime yang dipakai. Alokasi besar seperti itu mempercepat penuhnya generasi memori muda dan memaksa garbage collector, bagian runtime yang mengosongkan memori object yang tidak dirujuk lagi, bekerja lebih sering, sehingga program yang sama terasa lebih lambat dibandingkan versinya tanpa boxing.
Sumber boxing yang sering menyusup tanpa terlihat adalah pemakaian tipe nilai melalui antarmuka dan pemakaian koleksi non-generik. Penugasan IComparable pembanding = angka pada program di atas melakukan satu kali boxing, dan setiap pemanggilan CompareTo pada antarmuka itu kembali melakukan boxing untuk argumennya. Koleksi lama seperti ArrayList menyimpan semua elemennya sebagai object, sehingga memasukkan seribu angka ke dalamnya melakukan seribu kali boxing. Solusinya adalah koleksi generik seperti List<int> yang menyimpan angka apa adanya, metode generik daripada parameter object, serta pola tipe dengan is alih-alih konversi buta dari object.
Kesalahan umum#
- Membandingkan dua variabel
objectberisi angka dengan operator==dan mengharapkan perbandingan isi. Operator==padaobjectmembandingkan identitas, sehingga dua kotak berisi 42 tetap dianggap berbeda. - Unboxing ke tipe yang tidak persis sama, misalnya
(double)terhadap kotak berisiint. Prosesnya gagal denganInvalidCastException, dan tipe sasaran harus cocok terlebih dahulu sebelum konversi angka dilakukan. - Menyimpan angka dalam koleksi bertipe
objectlalu membacanya kembali untuk dihitung. Pola ini menimbulkan boxing pada setiap penyimpanan dan unboxing pada setiap pembacaan, sedangkanList<int>menghilangkan keduanya.
// Contoh: operator == pada object membandingkan identitas
object a = 42;
object b = 42;
Console.WriteLine($"a == b -> {a == b}");
Console.WriteLine($"a.Equals(b) -> {a.Equals(b)}");a == b -> False
a.Equals(b) -> TrueKeluaran menunjukkan bahwa a == b bernilai False karena == pada object menguji apakah keduanya satu object yang sama, sedangkan a.Equals(b) bernilai True karena metode Equals pada pembungkus int membandingkan isinya. Perbedaan ini sering menjadi sumber kebingungan ketika nilai bertipe nilai dipindah-pindahkan melalui variabel object, dan menjadi alasan tambahan untuk menghindari boxing bila tidak diperlukan.
Ringkasan#
- Boxing terjadi saat nilai bertipe nilai dikonversi diam-diam menjadi
objectatau antarmuka, dan setiap kejadiannya mengalokasikan object pembungkus baru di heap. - Unboxing memerlukan pengecoran eksplisit ke tipe yang persis sama, dan pelanggarannya memicu
InvalidCastExceptionsaat program berjalan. - Dampak performanya berasal dari alokasi dan beban garbage collector, dan pencegahannya adalah koleksi generik, metode generik, serta pemeriksaan tipe dengan
is.
Referensi#
- Boxing and Unboxing (C# Programming Guide) — Microsoft Learn — Panduan resmi kejadian boxing, aturan unboxing, dan catatan performanya.
- Value types (C# reference) — Microsoft Learn — Rujukan resmi kategori tipe nilai yang menjadi sasaran boxing.
- Reference types (C# reference) — Microsoft Learn — Rujukan resmi kategori tipe referensi tempat hasil boxing berada.