Beberapa pria menanyakan pertanyaan ini kepada saya beberapa bulan yang lalu dan saya tidak dapat menjelaskannya secara detail. Apa perbedaan antara tipe referensi dan tipe nilai di C #?
Saya tahu bahwa jenis nilai yang int
, bool
, float
, dll dan referensi jenis yang delegate
, interface
, dll Atau salah ini, juga?
Bisakah Anda menjelaskannya kepada saya secara profesional?
c#
.net
value-type
reference-type
tugberk
sumber
sumber
Jawaban:
Contoh Anda agak aneh karena sementara
int
,bool
danfloat
merupakan tipe tertentu, antarmuka dan delegasi adalah tipe tipe - sama sepertistruct
danenum
merupakan tipe tipe nilai.Saya telah menulis sebuah penjelasan tentang jenis referensi dan jenis nilai dalam artikel ini . Saya akan dengan senang hati menjelaskan setiap bagian yang menurut Anda membingungkan.
Versi "TL; DR" adalah memikirkan apa nilai variabel / ekspresi dari tipe tertentu. Untuk tipe nilai, nilainya adalah informasi itu sendiri. Untuk tipe referensi, nilainya adalah referensi yang mungkin nihil atau mungkin merupakan cara untuk menavigasi ke objek yang berisi informasi.
Misalnya, anggap variabel seperti selembar kertas. Itu bisa memiliki nilai "5" atau "false" yang tertulis di atasnya, tapi tidak bisa rumah saya ... itu harus memiliki petunjuk arah ke rumah saya. Arah tersebut setara dengan referensi. Secara khusus, dua orang dapat memiliki potongan kertas berbeda yang berisi petunjuk arah yang sama ke rumah saya - dan jika satu orang mengikuti petunjuk tersebut dan mengecat rumah saya dengan warna merah, orang kedua akan melihat perubahan itu juga. Jika mereka berdua hanya memiliki gambar terpisah dari rumah saya di atas kertas, maka satu orang yang mewarnai kertas mereka tidak akan mengubah kertas orang lain sama sekali.
sumber
while int, bool and float are specific types, interfaces and delegates are kinds of type - just like struct and enum are kinds of value types
. Apa yang Anda maksud dengan int, bool menjadi tipe tertentu? Segala sesuatu di C # misalnya int, bool, float, class, interface, delegate adalah tipe (tipe data tepatnya). Jenis data dipisahkan sebagai 'Jenis Referensi' dan 'Jenis Nilai' di C #. Lalu mengapa Anda mengatakan int adalah tipe tertentu tetapi antarmuka adalah jenis tipe?int
adalah struct,string
adalah kelas,Action
adalah delegasi, dll. Daftar "int, bool, float, class, interface, delegate" adalah daftar yang berisi berbagai macam hal, dengan cara yang sama seperti "10, int" adalah daftar yang berisi berbagai jenis hal.Jenis nilai:
Memegang beberapa nilai bukan alamat memori
Contoh:
Struct
Penyimpanan:
TL; DR : Nilai variabel disimpan di mana pun ia dideklarasikan. Variabel lokal hidup di tumpukan misalnya, tetapi ketika dideklarasikan di dalam kelas sebagai anggota, ia tinggal di heap yang digabungkan erat dengan kelas tempat ia dideklarasikan.
Longer : Jadi tipe nilai disimpan di mana pun mereka dideklarasikan. Misalnya: nilai an
int
di dalam fungsi sebagai variabel lokal akan disimpan di stack, sementaraint
nilai in yang dideklarasikan sebagai anggota dalam kelas akan disimpan di heap dengan kelas tempat ia dideklarasikan. Jenis nilai pada sebuah kelas memiliki jenis kehidupan yang persis sama dengan kelas yang dideklarasikan, hampir tidak memerlukan pekerjaan oleh pengumpul sampah. Ini lebih rumit, saya akan merujuk ke buku @ JonSkeet " C # In Depth "Memori dalam .NET"untuk penjelasan yang lebih ringkas.Keuntungan:
Jenis nilai tidak membutuhkan pengumpulan sampah tambahan. Itu mendapatkan sampah yang dikumpulkan bersama dengan instance tempat tinggalnya. Variabel lokal dalam metode dibersihkan setelah metode pergi.
Kekurangan:
Ketika sejumlah besar nilai diteruskan ke metode, variabel penerima sebenarnya menyalin sehingga ada dua nilai yang berlebihan dalam memori.
Karena kelas terlewat. Itu kehilangan semua manfaat luar
Jenis referensi:
Menyimpan alamat memori dengan nilai bukan nilai
Contoh:
Kelas
Penyimpanan:
Disimpan di heap
Keuntungan:
Ketika Anda meneruskan variabel referensi ke metode dan itu mengubahnya memang mengubah nilai asli sedangkan dalam tipe nilai salinan variabel yang diberikan diambil dan nilai itu diubah.
Jika ukuran variabel lebih besar, tipe referensi bagus
Karena kelas datang sebagai variabel jenis referensi, mereka memberikan usabilitas, sehingga menguntungkan pemrograman berorientasi objek
Kekurangan:
Lebih banyak referensi pekerjaan saat mengalokasikan dan dereferensi saat membaca overload value.extra untuk pengumpul sampah
sumber
Saya merasa lebih mudah untuk memahami perbedaan keduanya jika Anda tahu bagaimana komputer mengalokasikan barang dalam memori dan tahu apa itu pointer.
Referensi biasanya dikaitkan dengan penunjuk. Artinya alamat memori tempat variabel Anda berada sebenarnya menyimpan alamat memori lain dari objek sebenarnya di lokasi memori yang berbeda.
Contoh yang akan saya berikan terlalu disederhanakan, jadi ambillah dengan sebutir garam.
Bayangkan memori komputer adalah sekumpulan PO box dalam satu baris (mulai dengan PO Box 0001 hingga PO Box n) yang dapat menampung sesuatu di dalamnya. Jika PO box tidak melakukannya untuk Anda, coba hashtable atau kamus atau larik atau yang serupa.
Jadi, ketika Anda melakukan sesuatu seperti:
var a = "Halo";
komputer akan melakukan hal berikut:
Jadi ini semacam alias (0500 adalah a).
Jenis nilai akan menyimpan hal yang sebenarnya di lokasi memorinya.
Jadi, ketika Anda melakukan sesuatu seperti:
var a = 1;
komputer akan melakukan hal berikut:
Perhatikan bahwa kami tidak mengalokasikan memori tambahan untuk menampung nilai sebenarnya (1). Jadi a sebenarnya memegang nilai sebenarnya dan itulah mengapa disebut tipe nilai.
sumber
Ini dari postingan saya dari forum yang berbeda, sekitar dua tahun lalu. Meskipun bahasanya adalah vb.net (kebalikan dari C #), konsep Tipe Nilai vs. tipe Referensi seragam di seluruh .net, dan contoh masih berlaku.
Penting juga untuk diingat bahwa dalam .net, SEMUA tipe secara teknis diturunkan dari tipe dasar Object. Tipe nilai dirancang untuk berperilaku seperti itu, tetapi pada akhirnya mereka juga mewarisi fungsionalitas Objek tipe dasar.
A. Tipe Nilai hanyalah bahwa- mereka mewakili area yang berbeda dalam memori di mana sebuah NILAI diskrit disimpan. Jenis nilai memiliki ukuran memori tetap dan disimpan dalam tumpukan, yang merupakan kumpulan alamat dengan ukuran tetap.
Ketika Anda membuat pernyataan seperti itu:
Anda telah melakukan hal berikut:
Nilai setiap variabel ada secara terpisah di setiap lokasi memori.
B. Jenis Referensi dapat dalam berbagai ukuran. Oleh karena itu, mereka tidak dapat disimpan di "Stack" (ingat, stack adalah kumpulan alokasi memori dengan ukuran tetap?). Mereka disimpan di "Heap Terkelola". Pointer (atau "referensi") ke setiap item di heap terkelola dipertahankan dalam tumpukan (Seperti Alamat). Kode Anda menggunakan petunjuk ini dalam tumpukan untuk mengakses objek yang disimpan di heap terkelola. Jadi, saat kode Anda menggunakan variabel referensi, sebenarnya kode tersebut menggunakan pointer (atau "alamat" ke lokasi memori di heap terkelola).
Katakanlah Anda telah membuat Kelas bernama clsPerson, dengan string Property Person.Name
Dalam hal ini, ketika Anda membuat pernyataan seperti ini:
Dalam kasus di atas, Properti p1.Name akan Mengembalikan "Jim Morrison", seperti yang Anda harapkan. Properti p2.Name JUGA akan mengembalikan "Jim Morrison", seperti yang Anda harapkan secara intuitif. Saya yakin bahwa p1 dan p2 mewakili alamat yang berbeda di Stack. Namun, sekarang setelah Anda menetapkan p2 nilai p1, p1 dan p2 menunjuk ke LOKASI YANG SAMA di heap terkelola.
Sekarang pertimbangkan situasi INI:
Dalam situasi ini, Anda telah membuat satu contoh baru dari kelas orang di Heap Terkelola dengan penunjuk p1 di Tumpukan yang mereferensikan objek, dan menetapkan Properti Nama contoh objek nilai "Jim Morrison" lagi. Selanjutnya, Anda membuat pointer lain p2 di Stack, dan mengarahkannya ke alamat yang sama di heap terkelola seperti yang direferensikan oleh p1 (saat Anda membuat tugas p2 = p1).
Inilah twistnya. Ketika Anda menetapkan properti Nama p2 nilai "Janis Joplin" Anda mengubah properti Nama untuk objek REFERENSI oleh p1 dan p2, sehingga, jika Anda menjalankan kode berikut:
Apakah itu masuk akal?
Terakhir. Jika kamu melakukan ini:
Anda sekarang memiliki dua Objek Orang yang berbeda. Namun, begitu Anda melakukan INI lagi:
Anda sekarang telah mengarahkan keduanya kembali ke "Jim Morrison". (Saya tidak begitu yakin apa yang terjadi pada Objek di Heap yang direferensikan oleh p2 ... Saya BERPIKIR sekarang sudah keluar dari ruang lingkup. Ini adalah salah satu area di mana semoga seseorang dapat meluruskan saya ...). -EDIT: SAYA PERCAYA inilah mengapa Anda harus Set p2 = Nothing OR p2 = New clsPerson sebelum membuat tugas baru.
Sekali lagi, jika Anda sekarang melakukan INI:
Kedua msgBox sekarang akan menampilkan "Jimi Hendrix"
Ini bisa sedikit membingungkan, dan saya akan mengatakan untuk terakhir kalinya, saya mungkin memiliki beberapa detail yang salah.
Good Luck, dan semoga orang lain yang lebih tahu dari saya akan datang untuk membantu mengklarifikasi beberapa hal ini. . .
sumber
tipe data nilai dan tipe data referensi
1) nilai (berisi data secara langsung) tetapi referensi (mengacu pada data)
2) dalam nilai (setiap variabel memiliki salinannya sendiri) tetapi
dalam referensi (lebih dari variabel dapat merujuk ke beberapa objek)
3) dalam nilai (variabel operasi tidak dapat berpengaruh pada variabel lain) tetapi dalam referensi (variabel dapat mempengaruhi lainnya)
4) tipe nilai adalah (int, bool, float) tetapi tipe referensi adalah (array, objek kelas, string)
sumber
Jenis Nilai:
Ukuran memori tetap.
Disimpan dalam memori Stack.
Memiliki nilai sebenarnya.
Ex. int, char, bool, dll ...
Jenis Referensi:
Bukan memori tetap.
Disimpan dalam memori Heap.
Menyimpan alamat memori dari nilai sebenarnya.
Ex. string, array, kelas, dll ...
sumber
"Variabel yang didasarkan pada jenis nilai secara langsung berisi nilai. Menetapkan satu variabel jenis nilai ke variabel lain akan menyalin nilai yang terkandung. Ini berbeda dengan penetapan variabel jenis referensi, yang menyalin referensi ke objek tetapi tidak ke objek itu sendiri." dari perpustakaan Microsoft.
Anda dapat menemukan jawaban yang lebih lengkap di sini dan di sini .
sumber
Terkadang penjelasan tidak akan membantu terutama untuk pemula. Anda dapat membayangkan tipe nilai sebagai file data dan tipe referensi sebagai pintasan ke file.
Jadi jika Anda menyalin variabel referensi, Anda hanya menyalin tautan / penunjuk ke data nyata di suatu tempat di memori. Jika Anda menyalin tipe nilai, Anda benar-benar mengkloning data di memori.
sumber
Ini mungkin salah dalam hal esoteris, tetapi, untuk membuatnya sederhana:
Jenis nilai adalah nilai yang diteruskan secara normal "menurut nilai" (jadi menyalinnya). Jenis referensi diteruskan "dengan referensi" (jadi memberikan penunjuk ke nilai asli). Tidak ada jaminan apa pun oleh standar ECMA .NET di mana "hal-hal" ini disimpan. Anda dapat membangun implementasi .NET yang tanpa tumpukan, atau yang tanpa tumpukan (yang kedua akan sangat rumit, tetapi Anda mungkin bisa, menggunakan serat dan banyak tumpukan)
Struktur adalah tipe nilai (int, bool ... adalah struct, atau setidaknya disimulasikan sebagai ...), kelas adalah tipe referensi.
Jenis nilai diturunkan dari System.ValueType. Jenis referensi turun dari System.Object.
Sekarang .. Pada akhirnya Anda memiliki Jenis Nilai, "objek yang direferensikan" dan referensi (dalam C ++ mereka akan disebut penunjuk ke objek. Dalam .NET mereka buram. Kami tidak tahu apa itu. Dari sudut pandang kami, mereka adalah "pegangan" ke objek). Yang terakhir ini mirip dengan Jenis Nilai (mereka diteruskan dengan salinan). Jadi sebuah objek disusun oleh objek (tipe referensi) dan nol atau lebih referensi ke sana (yang mirip dengan tipe nilai). Jika tidak ada referensi, GC mungkin akan mengumpulkannya.
Secara umum (dalam implementasi "default" dari .NET), Jenis nilai dapat ditempatkan di tumpukan (jika merupakan kolom lokal) atau di heap (jika merupakan kolom kelas, jika merupakan variabel dalam fungsi iterator, jika mereka adalah variabel yang direferensikan oleh closure, jika mereka adalah variabel dalam fungsi async (menggunakan Async CTP yang lebih baru) ...). Nilai yang direferensikan hanya dapat masuk ke heap. Referensi menggunakan aturan yang sama seperti tipe Nilai.
Dalam kasus Tipe Nilai yang berada di heap karena berada dalam fungsi iterator, fungsi asinkron, atau direferensikan oleh closure, jika Anda melihat file yang dikompilasi, Anda akan melihat bahwa kompilator membuat kelas untuk meletakkan variabel-variabel ini , dan kelas dibuat saat Anda memanggil fungsi tersebut.
Sekarang, saya tidak tahu bagaimana menulis hal-hal yang panjang, dan saya memiliki hal-hal yang lebih baik untuk dilakukan dalam hidup saya. Jika Anda menginginkan versi yang "tepat" "akademis" "benar", baca INI:
http://blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx
Ini 15 menit saya mencarinya! Ini lebih baik daripada versi msdn, karena ini adalah artikel "siap digunakan" yang ringkas.
sumber
Cara termudah untuk memikirkan jenis referensi adalah dengan menganggapnya sebagai "ID objek"; satu-satunya hal yang dapat dilakukan dengan ID objek adalah membuat satu, menyalin satu, menanyakan atau memanipulasi tipe satu, atau membandingkan dua untuk persamaan. Upaya untuk melakukan hal lain dengan ID objek akan dianggap sebagai cara cepat untuk melakukan tindakan yang ditunjukkan dengan objek yang dirujuk oleh id tersebut.
Misalkan saya memiliki dua variabel X dan Y tipe Mobil - tipe referensi. Y kebetulan memiliki "ID objek # 19531". Jika saya mengatakan "X = Y", itu akan menyebabkan X menyimpan "ID objek # 19531". Perhatikan bahwa baik X maupun Y tidak memiliki mobil. Mobil, atau dikenal sebagai "ID objek # 19531", disimpan di tempat lain. Ketika saya menyalin Y ke X, yang saya lakukan hanyalah menyalin nomor ID. Sekarang misalkan saya mengatakan X.Color = Colors.Blue. Pernyataan seperti itu akan dianggap sebagai instruksi untuk mencari "ID objek # 19531" dan mengecatnya dengan warna biru. Perhatikan bahwa meskipun X dan Y sekarang merujuk ke mobil biru daripada kuning, pernyataan itu sebenarnya tidak memengaruhi X atau Y, karena keduanya masih mengacu pada "ID objek # 19531", yang masih mobil yang sama seperti itu selalu begitu.
sumber
Jenis variabel dan Nilai Referensi mudah diterapkan dan diterapkan dengan baik pada model domain, memfasilitasi proses pengembangan.
Untuk menghilangkan mitos apapun seputar jumlah "tipe nilai", saya akan berkomentar tentang bagaimana hal ini ditangani di platform. NET, khususnya di C # (CSharp) ketika dipanggil APIS dan mengirim parameter berdasarkan nilai, dengan referensi, dalam metode kami, dan fungsi dan bagaimana membuat perlakuan yang benar dari bagian-bagian dari nilai-nilai ini.
Baca artikel ini Nilai Jenis Variabel dan Referensi di C #
sumber
Misalkan
v
adalah ekspresi / variabel tipe nilai, danr
merupakan ekspresi / variabel tipe referensiJadi, variabel tipe nilai menyimpan nilai sebenarnya (5, atau "h"). Variabel tipe referensi hanya menyimpan tautan ke kotak metaforis di mana nilainya berada.
sumber
Sebelum menjelaskan tipe data berbeda yang tersedia di C #, penting untuk menyebutkan bahwa C # adalah bahasa yang diketik dengan kuat. Ini berarti bahwa setiap variabel, konstanta, parameter masukan, tipe kembalian dan secara umum setiap ekspresi yang dievaluasi ke suatu nilai, memiliki tipe.
Setiap tipe berisi informasi yang akan disematkan oleh kompilator ke dalam file yang dapat dieksekusi sebagai metadata yang akan digunakan oleh runtime bahasa umum (CLR) untuk menjamin keamanan tipe ketika mengalokasikan dan mengambil kembali memori.
Jika Anda ingin mengetahui berapa banyak memori yang dialokasikan oleh tipe tertentu, Anda dapat menggunakan ukuran operator sebagai berikut:
Keluarannya akan menunjukkan jumlah byte yang dialokasikan oleh setiap variabel.
Informasi yang terkait dengan masing-masing jenis adalah:
Anggota (metode, bidang, acara, dll.) Yang ada dalam tipe. Misalnya, jika kita memeriksa definisi tipe int, kita akan menemukan struct dan anggota berikut:
Manajemen memori Ketika beberapa proses berjalan pada sistem operasi dan jumlah RAM tidak cukup untuk menampung semuanya, sistem operasi memetakan bagian hard disk dengan RAM dan mulai menyimpan data di hard disk. Sistem operasi akan menggunakan tabel tertentu di mana alamat virtual dipetakan ke alamat fisik koresponden mereka untuk melakukan permintaan. Kemampuan untuk mengatur memori ini disebut memori virtual.
Dalam setiap proses, memori virtual yang tersedia diatur dalam 6 bagian berikut, tetapi untuk relevansi topik ini, kami hanya akan fokus pada tumpukan dan heap.
Tumpukan Tumpukan adalah struktur data LIFO (masuk terakhir, keluar pertama), dengan ukuran bergantung pada sistem operasi (secara default, untuk mesin ARM, x86 dan x64, Windows mencadangkan 1MB, sedangkan Linux memesan dari 2MB hingga 8MB tergantung pada Versi: kapan).
Bagian memori ini secara otomatis dikelola oleh CPU. Setiap kali suatu fungsi mendeklarasikan variabel baru, kompilator mengalokasikan blok memori baru sebesar ukurannya pada tumpukan, dan ketika fungsi selesai, blok memori untuk variabel tersebut dibatalkan alokasinya.
Tumpukan Wilayah memori ini tidak dikelola secara otomatis oleh CPU dan ukurannya lebih besar dari tumpukan. Ketika kata kunci baru dipanggil, kompilator mulai mencari blok memori bebas pertama yang sesuai dengan ukuran permintaan. dan ketika menemukannya, itu ditandai sebagai dicadangkan dengan menggunakan fungsi malloc () C bawaan dan mengembalikan penunjuk ke lokasi itu. Mungkin juga untuk membatalkan alokasi blok memori dengan menggunakan fungsi C bawaan free (). Mekanisme ini menyebabkan fragmentasi memori dan harus menggunakan pointer untuk mengakses blok memori yang tepat, ini lebih lambat daripada stack untuk melakukan operasi baca / tulis.
Tipe Khusus dan Bawaan Meskipun C # menyediakan satu set standar tipe bawaan yang mewakili bilangan bulat, boolean, karakter teks, dan seterusnya, Anda dapat menggunakan konstruksi seperti struct, class, antarmuka, dan enum untuk membuat tipe Anda sendiri.
Contoh tipe kustom menggunakan konstruksi struct adalah:
Tipe nilai dan referensi Kita dapat mengkategorikan tipe C # ke dalam kategori berikut:
Jenis nilai nilai berasal dari kelas System.ValueType dan variabel tipe ini berisi nilai mereka dalam alokasi memorinya di tumpukan. Dua kategori tipe nilai adalah struct dan enum.
Contoh berikut menunjukkan anggota tipe boolean. Seperti yang Anda lihat, tidak ada referensi eksplisit ke kelas System.ValueType, ini terjadi karena kelas ini diwarisi oleh struct.
Jenis referensi Di sisi lain, jenis referensi tidak berisi data aktual yang disimpan dalam variabel, tetapi alamat memori dari heap tempat nilai disimpan. Kategori tipe referensi adalah kelas, delegasi, array, dan antarmuka.
Pada waktu proses, ketika variabel tipe referensi dideklarasikan, itu berisi nilai null hingga objek yang telah dibuat menggunakan kata kunci baru ditugaskan padanya.
Contoh berikut menunjukkan anggota Daftar tipe generik.
Jika Anda ingin mengetahui alamat memori objek tertentu, class System.Runtime.InteropServices menyediakan cara untuk mengakses objek yang dikelola dari memori yang tidak dikelola. Dalam contoh berikut, kita akan menggunakan metode statis GCHandle.Alloc () untuk mengalokasikan pegangan ke string dan kemudian metode AddrOfPinnedObject untuk mengambil alamatnya.
Outputnya adalah
Referensi Dokumentasi resmi: https://docs.microsoft.com/en-us/cpp/build/reference/stack-stack-allocations?view=vs-2019
sumber
Ada banyak detail kecil tentang perbedaan antara tipe nilai dan tipe referensi yang dinyatakan secara eksplisit oleh standar dan beberapa di antaranya tidak mudah dipahami, terutama bagi pemula.
Lihat standar ECMA 33, Common Language Infrastructure (CLI) . CLI juga distandarisasi oleh ISO. Saya akan memberikan referensi tetapi untuk ECMA kita harus mengunduh PDF dan tautan itu tergantung pada nomor versinya. Standar ISO membutuhkan biaya.
Satu perbedaan adalah bahwa tipe nilai dapat dikotakkan tetapi tipe referensi umumnya tidak bisa. Ada pengecualian tetapi cukup teknis.
Jenis nilai tidak boleh memiliki konstruktor atau finalizer instance tanpa parameter dan tidak dapat merujuk ke dirinya sendiri. Merujuk pada dirinya sendiri berarti misalnya jika ada Node yang berjenis nilai maka salah satu anggota Node tidak bisa menjadi Node . Saya rasa ada persyaratan / batasan lain dalam spesifikasi tetapi jika demikian maka mereka tidak dikumpulkan di satu tempat.
sumber