Saya tahu bahwa "string" dalam C # adalah tipe referensi. Ini di MSDN. Namun, kode ini tidak berfungsi sebagaimana mestinya:
class Test
{
public static void Main()
{
string test = "before passing";
Console.WriteLine(test);
TestI(test);
Console.WriteLine(test);
}
public static void TestI(string test)
{
test = "after passing";
}
}
Keluaran harus "sebelum meneruskan" "setelah lewat" karena saya meneruskan string sebagai parameter dan itu menjadi tipe referensi, pernyataan keluaran kedua harus mengenali bahwa teks berubah dalam metode TestI. Namun, saya mendapatkan "sebelum lewat" "sebelum lewat" membuatnya seolah-olah nilai itu diteruskan oleh nilai bukan oleh ref. Saya mengerti bahwa string tidak dapat diubah, tetapi saya tidak melihat bagaimana hal itu akan menjelaskan apa yang sedang terjadi di sini. Apa yang saya lewatkan? Terima kasih.
Jawaban:
Referensi ke string dilewatkan oleh nilai. Ada perbedaan besar antara melewatkan referensi dengan nilai dan melewati objek dengan referensi. Sangat disayangkan bahwa kata "referensi" digunakan dalam kedua kasus.
Jika Anda melakukan lulus string referensi oleh referensi, ia akan bekerja seperti yang Anda harapkan:
Sekarang Anda perlu membedakan antara membuat perubahan pada objek yang dirujuk oleh referensi, dan membuat perubahan pada variabel (seperti parameter) untuk membiarkannya merujuk ke objek yang berbeda. Kami tidak dapat membuat perubahan pada string karena string tidak dapat diubah, tetapi kami dapat mendemonstrasikannya dengan
StringBuilder
:Lihat artikel saya tentang parameter yang lewat untuk detail lebih lanjut.
sumber
referenced
menjawabnyaJika kita harus menjawab pertanyaan: String adalah tipe referensi dan berperilaku sebagai referensi. Kami melewati parameter yang menyimpan referensi, bukan string yang sebenarnya. Masalahnya ada di fungsi:
Parameter
test
menyimpan referensi ke string tetapi itu adalah salinan. Kami memiliki dua variabel yang menunjuk ke string. Dan karena operasi apa pun dengan string benar-benar membuat objek baru, kami membuat salinan lokal kami untuk menunjuk ke string baru. Tetapitest
variabel asli tidak berubah.Solusi yang disarankan untuk dimasukkan ke
ref
dalam deklarasi fungsi dan dalam pekerjaan pemanggilan karena kita tidak akan melewatkan nilaitest
variabel tetapi hanya akan meneruskan referensi ke sana. Jadi setiap perubahan di dalam fungsi akan mencerminkan variabel asli.Saya ingin mengulangi di akhir: String adalah tipe referensi tetapi karena tidak berubah baris
test = "after passing";
sebenarnya menciptakan objek baru dan salinan variabel kamitest
diubah untuk menunjuk ke string baru.sumber
Seperti yang telah dinyatakan oleh orang lain,
String
tipe dalam. NET tidak dapat diubah dan rujukannya dilewatkan oleh nilai.Dalam kode asli, segera setelah baris ini dijalankan:
maka
test
tidak lagi mengacu pada objek asli. Kami telah membuat objek baruString
dan ditugaskantest
untuk referensi objek itu di tumpukan yang dikelola.Saya merasa bahwa banyak orang tersandung di sini karena tidak ada konstruktor formal yang terlihat untuk mengingatkan mereka. Dalam hal ini, itu terjadi di belakang layar sejak
String
jenisnya memiliki dukungan bahasa dalam cara membangunnya.Karenanya, inilah mengapa perubahan menjadi
test
tidak terlihat di luar cakupanTestI(string)
metode - kami telah melewati referensi berdasarkan nilai dan sekarang nilai tersebut telah berubah! Tetapi jikaString
referensi disahkan oleh referensi, maka ketika referensi berubah kita akan melihatnya di luar ruang lingkupTestI(string)
metode.Entah ref atau keluar kata kunci yang diperlukan dalam hal ini. Saya merasa
out
kata kunci mungkin sedikit lebih cocok untuk situasi khusus ini.sumber
out
perlu ditetapkan dalam metode untuk dikompilasi kode.ref
tidak memiliki persyaratan seperti itu. Jugaout
parameter diinisialisasi luar metode - kode dalam jawaban ini adalah counterexample.out
parameter dapat diinisialisasi di luar metode, tetapi tidak harus. Dalam hal ini, kami ingin menginisialisasiout
parameter untuk menunjukkan titik tentang sifatstring
tipe .NET.Sebenarnya itu akan sama untuk objek apa pun dalam hal ini yaitu menjadi tipe referensi dan lewat referensi adalah 2 hal berbeda dalam c #.
Ini akan berhasil, tetapi itu berlaku terlepas dari jenis:
Juga tentang string yang menjadi tipe referensi, itu juga yang spesial. Ini dirancang agar tidak berubah, sehingga semua metode tidak akan mengubah instance (mereka mengembalikan yang baru). Ini juga memiliki beberapa hal tambahan untuk kinerja.
sumber
Berikut cara yang baik untuk memikirkan perbedaan antara tipe-nilai, passing-by-value, tipe-referensi, dan passing-oleh-referensi:
Variabel adalah wadah.
Variabel tipe nilai berisi instance. Variabel tipe referensi berisi pointer ke instance yang disimpan di tempat lain.
Memodifikasi variabel tipe-nilai bermutasi instance yang dikandungnya. Memodifikasi variabel tipe referensi bermutasi contoh yang menunjuk.
Variabel tipe referensi yang terpisah dapat menunjuk ke instance yang sama. Oleh karena itu, instance yang sama dapat dimutasi melalui variabel apa pun yang menunjuk ke sana.
Argumen lewat-nilai adalah wadah baru dengan salinan konten yang baru. Argumen lulus-oleh-referensi adalah wadah asli dengan konten aslinya.
Ketika argumen tipe nilai diteruskan oleh nilai: Menugaskan kembali konten argumen tidak berpengaruh di luar cakupan, karena wadah itu unik. Mengubah argumen tidak berpengaruh di luar cakupan, karena instance adalah salinan independen.
Ketika argumen tipe referensi diteruskan oleh nilai: Menugaskan kembali konten argumen tidak memiliki pengaruh di luar ruang lingkup, karena wadah itu unik. Memodifikasi konten argumen memengaruhi lingkup eksternal, karena penunjuk yang disalin menunjuk ke instance bersama.
Ketika ada argumen yang dilewatkan oleh referensi: Menugaskan kembali konten argumen mempengaruhi ruang lingkup eksternal, karena wadah dibagikan. Memodifikasi konten argumen memengaruhi lingkup eksternal, karena konten dibagikan.
Kesimpulannya:
Variabel string adalah variabel tipe referensi. Oleh karena itu, ini berisi pointer ke instance yang disimpan di tempat lain. Ketika diteruskan oleh nilai, penunjuknya disalin, jadi memodifikasi argumen string harus memengaruhi instance yang dibagikan. Namun, turunan string tidak memiliki sifat yang dapat diubah, sehingga argumen string tidak dapat dimodifikasi. Ketika lulus-oleh-referensi, wadah pointer dibagikan, sehingga penugasan kembali masih akan mempengaruhi lingkup eksternal.
sumber
" Sebuah gambar bernilai ribuan kata ".
Saya punya contoh sederhana di sini, mirip dengan kasus Anda.
Inilah yang terjadi:
s1
dans2
referensi variabel sama"abc"
objek string yang ."abc"
objek string tidak memodifikasi sendiri (to"def"
), tetapi"def"
objek string baru dibuat sebagai gantinya, dan kemudians1
merujuknya.s2
masih merujuk ke"abc"
objek string, jadi itulah outputnya.sumber
Jawaban di atas sangat membantu, saya hanya ingin menambahkan contoh yang saya pikir menunjukkan dengan jelas apa yang terjadi ketika kita melewati parameter tanpa kata kunci ref, bahkan ketika parameter itu adalah tipe referensi:
sumber
Untuk pikiran yang ingin tahu dan untuk menyelesaikan percakapan: Ya, String adalah tipe referensi :
Tetapi perhatikan bahwa perubahan ini hanya berfungsi di blok yang tidak aman ! karena String tidak dapat diubah (Dari MSDN):
Dan perlu diingat bahwa:
sumber
Saya percaya kode Anda analog dengan yang berikut ini, dan Anda seharusnya tidak mengharapkan nilai telah berubah karena alasan yang sama tidak ada di sini:
sumber
Mencoba:
sumber