
Foto oleh Brett Jordan via Pexels
Tipe data pada C# menentukan jenis nilai apa yang bisa disimpan sebuah variabel beserta operasi apa saja yang valid dilakukan terhadapnya. C# membagi tipe data menjadi dua kategori besar: value type yang menyimpan nilai secara langsung, dan reference type yang menyimpan alamat memori menuju data sebenarnya.
Tipe data primitif seperti int, double, bool, dan char adalah value type, sementara string, array, dan class adalah reference type. Perbedaan ini bukan sekadar teknis, tetapi memengaruhi bagaimana data tersebut berperilaku saat disalin ke variabel lain atau dikirim sebagai parameter method.
Artikel ini membahas tipe data dasar yang paling sering dipakai, perbedaan fundamental antara value type dan reference type, serta konsep type inference dengan kata kunci var yang sering membingungkan pemula.
Tipe Data Numerik dan Jangkauannya
Pemilihan tipe numerik sesuai kebutuhan presisi dan jangkauan
Value Type Disalin Secara Independen
Mengubah salinan value type tidak memengaruhi aslinya
Reference Type Berbagi Lokasi Memori yang Sama
Mengubah reference type lewat satu variabel memengaruhi variabel lain yang menunjuk ke objek sama
Type Inference dengan var
var tetap strongly-typed, hanya tipe disimpulkan compiler
Perbedaan Value Type dan Reference Type dalam Alokasi Memori
struct (value type) vs class (reference type) saat disalin
Tipe data pada C# terbagi menjadi dua kategori besar berdasarkan cara penyimpanannya di memori: value type dan reference type. Tipe seperti int, double, bool, char, dan struct termasuk value type yang disimpan langsung pada stack dan menyimpan nilainya sendiri, sehingga ketika sebuah variabel value type disalin ke variabel lain, yang terjadi adalah penyalinan nilai secara penuh dan independen satu sama lain, seperti terlihat pada variabel p1 dan p2 di atas.
Sebaliknya, class termasuk reference type yang objeknya disimpan pada heap, sementara variabel yang mereferensikannya hanya menyimpan alamat memori (reference) menuju objek tersebut. Ketika b1 disalin ke b2, keduanya menunjuk ke objek yang sama di heap, sehingga perubahan melalui b2 juga terlihat melalui b1. Perbedaan perilaku ini sering menjadi sumber bug tersembunyi bagi pemula yang mengasumsikan semua penyalinan variabel bersifat independen.
Memahami perbedaan ini juga penting untuk pertimbangan performa: value type yang berukuran besar dan sering disalin dapat membebani stack, sedangkan reference type yang terlalu banyak dibuat akan membebani garbage collector karena harus melacak objek-objek di heap yang sudah tidak digunakan lagi untuk dibersihkan secara berkala.
Konversi Tipe Data: Implicit, Explicit Casting, dan Parsing String
Implicit casting otomatis, explicit casting manual, serta Parse vs TryParse
Konversi tipe data pada C# terbagi menjadi implicit dan explicit casting. Implicit casting terjadi otomatis tanpa sintaks khusus ketika tipe tujuan mampu menampung seluruh kemungkinan nilai dari tipe asal tanpa risiko kehilangan data, seperti konversi int ke double pada contoh di atas. Sebaliknya, explicit casting menggunakan tanda kurung berisi nama tipe tujuan dan wajib dituliskan manual karena ada risiko kehilangan presisi atau data, seperti konversi double ke int yang memotong bagian desimalnya.
Untuk mengonversi data berbentuk string menjadi tipe numerik, method Parse seperti int.Parse akan melempar FormatException jika string yang diberikan tidak sesuai format angka yang valid, sehingga pemanggilannya sebaiknya dibungkus try-catch atau, lebih disarankan, memakai method TryParse yang mengembalikan nilai boolean penanda keberhasilan tanpa melempar exception sama sekali, dengan hasil konversi diletakkan pada parameter out seperti hasilTryParse pada contoh di atas.
Method Convert.ToInt32 merupakan alternatif lain yang mirip dengan Parse namun memiliki perilaku berbeda terhadap nilai null, karena Convert.ToInt32(null) akan mengembalikan 0 alih-alih melempar exception seperti yang dilakukan int.Parse(null). Pemilihan antara Parse, TryParse, dan Convert perlu disesuaikan dengan konteks, terutama ketika data berasal dari input pengguna atau sumber eksternal yang formatnya tidak selalu dapat dijamin valid, sehingga TryParse umumnya menjadi pilihan paling aman untuk kasus semacam itu. Casting yang tidak tepat antar tipe numerik, misalnya dari long ke int tanpa pengecekan rentang nilai, juga dapat menimbulkan overflow diam-diam yang menghasilkan nilai tidak terduga tanpa melempar exception sama sekali kecuali blok checked secara eksplisit digunakan.
Perbedaan const dan readonly dalam Deklarasi Konstanta
Kata kunci const digunakan untuk nilai yang sudah diketahui dan tetap sama sejak waktu kompilasi, seperti angka pajak atau nama hari dalam seminggu, dan nilainya tertanam langsung ke dalam kode hasil kompilasi sehingga tidak bisa diubah sama sekali, bahkan oleh instance class itu sendiri karena sifatnya implicit static dan hanya bisa diakses melalui nama class, bukan melalui instance object seperti k.PajakPersen. Karena nilainya disalin langsung ke tempat pemakaian saat kompilasi, mengubah nilai const di kemudian hari mengharuskan semua kode yang memakainya dikompilasi ulang agar perubahan benar-benar diterapkan.
Sebaliknya, readonly mengizinkan nilai ditentukan saat runtime, biasanya melalui constructor seperti WaktuDibuat pada contoh di atas, dan nilainya boleh berbeda untuk tiap instance object meskipun setelah constructor selesai dijalankan nilainya tidak bisa diubah lagi dari luar. Readonly cocok dipakai ketika nilai konstan bergantung pada kondisi tertentu saat object dibuat, seperti waktu pembuatan object, hasil perhitungan awal, atau nilai yang diterima dari parameter constructor yang berbeda-beda untuk setiap instance, sehingga setiap object bisa memiliki nilai readonly yang unik sesuai konteks pembuatannya masing-masing.
Kesalahan Umum yang Sering Terjadi
Memakai double untuk Perhitungan Uang
Tipe double bisa menghasilkan kesalahan pembulatan kecil pada operasi aritmatika berulang, yang jika diakumulasikan pada perhitungan finansial bisa menghasilkan selisih yang signifikan dibanding memakai decimal.
Mengira var Membuat C# Menjadi Dynamically-Typed
Beberapa pemula mengira var memungkinkan variabel diisi ulang dengan tipe data berbeda di kemudian hari, padahal tipe tetap ditentukan secara permanen saat deklarasi pertama berdasarkan nilai awal yang diberikan.
Tidak Memahami Konsekuensi Berbagi Reference pada Array atau Object
Menyalin array atau object ke variabel lain dengan asumsi keduanya independen seperti value type bisa menyebabkan bug yang membingungkan ketika perubahan pada satu variabel ternyata ikut mengubah variabel lain yang dikira terpisah.
Kapan Pendekatan Ini Tidak Cocok Dipakai
Value type lebih efisien untuk data kecil dan sederhana karena tidak memerlukan alokasi memori tambahan di heap serta tidak perlu ditangani garbage collector, cocok untuk tipe seperti int, bool, atau struct kecil.
Reference type lebih fleksibel untuk struktur data kompleks yang perlu dibagikan antar bagian program tanpa menyalin seluruh isinya, namun membutuhkan pemahaman yang jelas tentang kapan data benar-benar disalin dan kapan hanya referensinya yang disalin untuk menghindari bug yang tidak disengaja.
Ringkasan
Memahami perbedaan value type dan reference type adalah salah satu fondasi paling penting dalam C#, karena memengaruhi cara data berperilaku saat disalin atau dikirim sebagai parameter. Pilih tipe numerik yang sesuai kebutuhan presisi, dan selalu sadari apakah tipe yang dipakai adalah value atau reference type sebelum menulis logika yang bergantung pada penyalinan data.
Seri Belajar C# Fundamental:
← Sebelumnya: Cara Membuat New Project Pemrograman C# (sudah ada)
→ Selanjutnya: Operasi Penyeleksian Kondisi (IF) dan Proses Perulangan (Loop) (sudah ada)