Menyalin data di C# mengikuti dua aturan berbeda. Value type (struct, int, bool, enum) disalin utuh berdasarkan nilai, sedangkan reference type (class, array, string, delegate) hanya menyalin alamatnya. Perbedaan ini disebut value semantics dan reference semantics, dan keduanya menentukan jumlah objek yang muncul di managed heap. Salah membaca aturan ini adalah sumber kejutan memori yang paling sering dialami pemula.
Contoh: Penyalinan Struct versus Class #
var a = new Titik { X = 1, Y = 2 };
var b = a;
b.X = 100;
Console.WriteLine($"struct: a=({a.X}, {a.Y}), b=({b.X}, {b.Y})");
var p = new TitikRef { X = 1, Y = 2 };
var q = p;
q.X = 100;
Console.WriteLine($"class : p=({p.X}, {p.Y}), q=({q.X}, {q.Y})");Output yang Dihasilkan:
struct: a=(1, 2), b=(100, 2)
class : p=(100, 2), q=(100, 2)Mari kita bedah baris demi baris:
var b = a;: runtime menyalin seluruh isi structake variabel barub. Kini ada dua salinan data yang berdiri sendiri, sehingga perubahan padabtidak menyentuha.b.X = 100;: hanya salinanbyang berubah. Output membuktikanatetap bernilai(1, 2).var q = p;: yang disalin hanyalah referensi (alamat) ke objek yang sama di heap. Tidak ada objek kedua yang dibuat.q.X = 100;: perubahan menjangkau satu-satunya objek di heap, sehinggapdanqmelaporkan nilai yang sama.
Implikasi ke Memori #
Reference semantics membuat penggunaan memori hemat: satu objek besar dapat dipakai banyak variabel tanpa penyalinan data. Value semantics membuat data mandiri: setiap penugasan menduplikasi isi struct, dan struct berukuran besar menyalin banyak byte setiap kali berpindah variabel atau parameter. Runtime juga melakukan boxing ketika value type dipaksa menjadi objek di heap, dan setiap boxing menciptakan pekerjaan baru bagi garbage collector.
| Situasi | Pilihan yang hemat memori |
|---|---|
| data kecil, sering disalin | struct kecil (sekitar 16-24 byte) |
| data besar, dibagikan | class yang diakses lewat referensi |
| struct besar yang sering lewat parameter | kirim dengan in agar tanpa salinan |