Saya membuat sebuah fungsi di mana saya harus meneruskan objek agar dapat dimodifikasi oleh fungsi tersebut. Apa perbedaan antara:
public void myFunction(ref MyClass someClass)
dan
public void myFunction(out MyClass someClass)
Mana yang harus saya gunakan dan mengapa?
MyClass
akan menjadiclass
tipe, yaitu tipe referensi. Dalam hal itu, objek yang Anda lewati dapat dimodifikasi olehmyFunction
genap tanparef
/out
kata kunci.myFunction
akan menerima referensi baru yang menunjuk ke objek yang sama , dan dapat memodifikasi objek yang sama sebanyak yang diinginkan. Perbedaanref
kata kunci akan membuat, akanmyFunction
menerima referensi yang sama untuk objek yang sama. Itu akan menjadi penting hanya jikamyFunction
mengubah referensi untuk menunjuk ke objek lain .Jawaban:
ref
memberitahu kompiler bahwa objek diinisialisasi sebelum memasuki fungsi, sementaraout
memberitahu kompiler bahwa objek akan diinisialisasi di dalam fungsi.Jadi, sementara
ref
dua arah,out
hanya keluar.sumber
The
ref
berarti modifikator yang:The
out
berarti modifikator yang:sumber
out
, dapatkah itu dibaca sama sekali di dalam metode, sebelum ditetapkan oleh metode itu, jika telah diinisialisasi sebelum metode dipanggil? Maksud saya, bisakah metode yang dipanggil membaca metode panggilan yang diteruskan sebagai argumen?Katakanlah Dom muncul di bilik Peter tentang memo tentang laporan TPS.
Jika Dom adalah argumen ref, ia akan memiliki salinan memo yang dicetak.
Jika Dom tidak setuju, dia akan membuat Peter mencetak salinan memo yang baru untuknya bawa.
sumber
Saya akan mencoba penjelasan saya:
Saya pikir kita mengerti bagaimana tipe nilai bekerja dengan benar? Jenis nilai adalah (int, long, struct dll). Ketika Anda mengirim mereka ke suatu fungsi tanpa perintah ref itu COPIES data . Apa pun yang Anda lakukan terhadap data itu dalam fungsi hanya memengaruhi salinan, bukan yang asli. Perintah ref mengirimkan data AKTUAL dan setiap perubahan akan memengaruhi data di luar fungsi.
Oke ke bagian yang membingungkan, jenis referensi:
Mari kita buat jenis referensi:
Saat Anda baru memulai suatu objek , dua bagian dibuat:
Sekarang ketika Anda mengirim objek ke suatu metode tanpa ref COPIES itu adalah referensi pointer, BUKAN data. Jadi sekarang Anda memiliki ini:
Dua referensi menunjuk ke objek yang sama. Jika Anda memodifikasi properti pada suatu objek menggunakan reference2 itu akan mempengaruhi data yang sama yang ditunjukkan oleh reference1.
Jika Anda membatalkan referensi2 atau mengarahkannya ke data baru, itu tidak akan mempengaruhi reference1 atau data reference1 menunjuk ke.
Sekarang apa yang terjadi ketika Anda mengirim objek dengan ref ke suatu metode? The referensi aktual untuk SomeObject akan dikirim ke metode. Jadi sekarang Anda hanya memiliki satu referensi ke data:
Tapi apa artinya ini? Kerjanya persis sama dengan mengirim objek bukan oleh ref kecuali untuk dua hal utama:
1) Ketika Anda membatalkan referensi di dalam metode itu akan membatalkan yang di luar metode.
2) Sekarang Anda dapat mengarahkan referensi ke lokasi data yang sama sekali berbeda dan referensi di luar fungsi sekarang akan menunjuk ke lokasi data baru.
sumber
ref
danout
parameter.out
kata kunci?ref masuk dan keluar .
Anda harus menggunakan
out
preferensi di mana pun itu mencukupi untuk kebutuhan Anda.sumber
di luar:
Dalam C #, metode hanya dapat mengembalikan satu nilai. Jika Anda ingin mengembalikan lebih dari satu nilai, Anda dapat menggunakan kata kunci keluar. Pengubah keluar kembali sebagai kembali-oleh-referensi. Jawaban paling sederhana adalah bahwa kata kunci "keluar" digunakan untuk mendapatkan nilai dari metode tersebut.
ref:
Di C #, ketika Anda melewati tipe nilai seperti int, float, double dll sebagai argumen ke parameter metode, itu dilewatkan oleh nilai. Oleh karena itu, jika Anda mengubah nilai parameter, itu tidak mempengaruhi argumen dalam pemanggilan metode. Tetapi jika Anda menandai parameter dengan kata kunci "ref", itu akan mencerminkan dalam variabel aktual.
sumber
Memperluas Anjing, contoh Kucing. Metode kedua dengan ref mengubah objek yang dirujuk oleh pemanggil. Karenanya "Kucing" !!!
sumber
Karena Anda memasukkan tipe referensi (kelas) tidak perlu digunakan
ref
karena per default hanya referensi ke objek aktual yang dilewati dan oleh karena itu Anda selalu mengubah objek di belakang referensi.Contoh:
Selama Anda lulus di kelas Anda tidak harus menggunakan
ref
jika Anda ingin mengubah objek di dalam metode Anda.sumber
someObject = null
untukBar
mengakhiri eksekusi. Kode Anda akan berjalan dengan baik karena hanyaBar
referensi ke instance yang dibatalkan. Sekarang ubahBar
menjadiBar(ref MyClass someObject)
dan jalankan lagi - Anda akan mendapatkan referensiNullReferenceException
karenaFoo
untuk instance telah dibatalkan juga.ref
danout
berperilaku sama kecuali mengikuti perbedaan.ref
variabel harus diinisialisasi sebelum digunakan.out
variabel dapat digunakan tanpa penugasanout
parameter harus diperlakukan sebagai nilai yang tidak ditetapkan oleh fungsi yang menggunakannya. Jadi, kita bisa menggunakanout
parameter yang diinisialisasi dalam kode panggilan, tetapi nilainya akan hilang ketika fungsi dijalankan.sumber
Bagi mereka yang belajar dengan contoh (seperti saya) inilah yang dikatakan Anthony Kolesov .
Saya telah membuat beberapa contoh minimal ref, out, dan lain-lain untuk menggambarkan intinya. Saya tidak membahas praktik terbaik, hanya contoh untuk memahami perbedaannya.
https://gist.github.com/2upmedia/6d98a57b68d849ee7091
sumber
"Tukang roti"
Itu karena yang pertama mengubah string-referensi Anda ke "Baker". Mengubah referensi dimungkinkan karena Anda meneruskannya melalui kata kunci ref (=> referensi ke referensi ke string). Panggilan kedua mendapat salinan referensi ke string.
string terlihat semacam spesial pada awalnya. Tetapi string hanyalah kelas referensi dan jika Anda mendefinisikan
maka s adalah referensi ke kelas string yang berisi teks "Mampu"! Tugas lain ke variabel yang sama via
tidak mengubah string asli tetapi hanya membuat instance baru dan mari kita tunjukkan ke instance itu!
Anda dapat mencobanya dengan contoh kode kecil berikut:
Apa yang kamu harapkan? Apa yang akan Anda dapatkan masih "Mampu" karena Anda hanya mengatur referensi dalam s ke instance lain sementara s2 menunjuk ke instance asli.
EDIT: string juga tidak dapat diubah yang berarti tidak ada metode atau properti yang memodifikasi instance string yang ada (Anda dapat mencoba menemukan satu di dokumen tetapi Anda tidak akan menemukan sirip :-)). Semua metode manipulasi string mengembalikan instance string baru! (Itu sebabnya Anda sering mendapatkan kinerja yang lebih baik saat menggunakan kelas StringBuilder)
sumber
ref berarti bahwa nilai dalam parameter ref sudah diatur, metode dapat membaca dan memodifikasinya. Menggunakan kata kunci ref sama dengan mengatakan bahwa penelepon bertanggung jawab untuk menginisialisasi nilai parameter.
keluar memberitahu compiler bahwa inisialisasi objek adalah tanggung jawab fungsi, fungsi memiliki untuk menetapkan ke parameter keluar. Tidak diizinkan membiarkannya tidak ditetapkan.
sumber
Di luar: Pernyataan pengembalian dapat digunakan untuk mengembalikan hanya satu nilai dari suatu fungsi. Namun, menggunakan parameter output, Anda dapat mengembalikan dua nilai dari suatu fungsi. Parameter output seperti parameter referensi, kecuali bahwa mereka mentransfer data dari metode daripada ke dalamnya.
Contoh berikut menggambarkan ini:
ref: Parameter referensi adalah referensi ke lokasi memori variabel. Ketika Anda melewatkan parameter dengan referensi, tidak seperti parameter nilai, lokasi penyimpanan baru tidak dibuat untuk parameter ini. Parameter referensi mewakili lokasi memori yang sama dengan parameter aktual yang disediakan untuk metode ini.
Di C #, Anda mendeklarasikan parameter referensi menggunakan kata kunci ref. Contoh berikut menunjukkan ini:
sumber
ref dan keluar bekerja seperti melewati referensi dan melewati pointer seperti pada C ++.
Untuk referensi, argumen harus dinyatakan dan diinisialisasi.
Untuk keluar, argumen harus dinyatakan tetapi mungkin atau mungkin tidak diinisialisasi
sumber
out double Half_nbr
.Waktu Penulisan:
(1) Kami membuat metode panggilan
Main()
(2) itu membuat objek Daftar (yang merupakan objek tipe referensi) dan menyimpannya dalam variabel
myList
.Selama Runtime:
(3) Runtime mengalokasikan memori pada tumpukan di # 00, cukup lebar untuk menyimpan alamat (# 00 =
myList
, karena nama variabel benar-benar hanya alias untuk lokasi memori)(4) Runtime membuat objek daftar di tumpukan di lokasi memori # FFF (semua alamat ini adalah contohnya)
(5) Runtime kemudian akan menyimpan alamat awal #FF dari objek di # 00 (atau dengan kata lain, menyimpan referensi objek Daftar di pointer
myList
)Kembali ke Waktu Penulisan:
(6) Kami kemudian meneruskan objek Daftar sebagai argumen
myParamList
ke metode yang dipanggilmodifyMyList
dan menetapkan objek Daftar baru untuk ituSelama Runtime:
(7) Runtime memulai rutinitas panggilan untuk metode yang dipanggil dan sebagai bagian dari itu, memeriksa jenis parameter.
(8) Setelah menemukan jenis referensi, itu mengalokasikan memori pada tumpukan di # 04 untuk alias variabel parameter
myParamList
.(9) Ia kemudian menyimpan nilai #FF di dalamnya juga.
(10) Runtime membuat objek daftar di tumpukan di lokasi memori # 004 dan menggantikan #FF di # 04 dengan nilai ini (atau men-dekereferensikan objek Daftar asli dan menunjuk ke objek Daftar baru dalam metode ini)
Alamat di # 00 tidak diubah dan mempertahankan referensi ke #FF (atau
myList
pointer asli tidak terganggu).Kata kunci ref adalah arahan kompiler untuk melewati pembuatan kode runtime untuk (8) dan (9) yang berarti tidak akan ada alokasi tumpukan untuk parameter metode. Ini akan menggunakan pointer # 00 asli untuk beroperasi pada objek di #FF. Jika pointer asli tidak diinisialisasi, runtime akan berhenti mengeluh itu tidak dapat dilanjutkan karena variabel tidak diinisialisasi
Kata kunci keluar adalah arahan kompiler yang hampir sama dengan ref dengan sedikit modifikasi pada (9) dan (10). Compiler mengharapkan argumen tidak diinisialisasi dan akan melanjutkan dengan (8), (4) dan (5) untuk membuat objek pada heap dan untuk menyimpan alamat awal dalam variabel argumen. Tidak ada kesalahan yang tidak diinisialisasi akan dilemparkan dan referensi yang tersimpan sebelumnya akan hilang.
sumber
Selain memungkinkan Anda untuk menetapkan kembali variabel orang lain ke instance kelas yang berbeda, mengembalikan beberapa nilai dll, menggunakan
ref
atauout
membiarkan orang lain tahu apa yang Anda butuhkan dari mereka dan apa yang ingin Anda lakukan dengan variabel yang mereka sediakanAnda tidak perlu
ref
atauout
jika semua yang akan Anda lakukan adalah memodifikasi hal-hal di dalamMyClass
instance yang diteruskan dalam argumensomeClass
.someClass.Message = "Hello World"
apakah Anda menggunakanref
,out
atau tidak sama sekalisomeClass = new MyClass()
di dalammyFunction(someClass)
menukar objek yang dilihat olehsomeClass
dalam lingkupmyFunction
metode saja. Metode panggilan masih tahu tentangMyClass
instance asli yang dibuat dan diteruskan ke metode AndaAnda perlu
ref
atauout
jika Anda berencana untuk menukarsomeClass
objek yang sama sekali baru dan ingin metode panggilan untuk melihat perubahan AndasomeClass = new MyClass()
di dalammyFunction(out someClass)
mengubah objek yang dilihat oleh metode yang disebutmyFunction
Ada programmer lain
Dan mereka ingin tahu apa yang akan Anda lakukan dengan data mereka. Bayangkan Anda sedang menulis perpustakaan yang akan digunakan oleh jutaan pengembang. Anda ingin mereka tahu apa yang akan Anda lakukan dengan variabel mereka ketika mereka memanggil metode Anda
Menggunakan
ref
membuat pernyataan "Lulus variabel yang ditugaskan ke beberapa nilai ketika Anda memanggil metode saya. Perlu diketahui bahwa saya mungkin mengubahnya untuk sesuatu yang lain sepenuhnya selama jalannya metode saya. Jangan berharap variabel Anda menunjuk ke objek yang lama ketika aku selesai "Menggunakan
out
membuat pernyataan "Lulus variabel tempat penampung ke metode saya. Tidak masalah apakah itu memiliki nilai atau tidak; kompiler akan memaksa saya untuk menetapkannya ke nilai baru. Saya benar-benar menjamin bahwa objek menunjuk oleh Anda variabel sebelum Anda memanggil metode saya, akan berbeda pada saat saya selesaiOmong-omong, di C # 7.2 ada
in
pengubah jugaDan itu mencegah metode dari menukar contoh lewat untuk contoh yang berbeda. Anggap saja seperti mengatakan kepada jutaan pengembang "berikan saya referensi variabel asli Anda, dan saya berjanji untuk tidak menukar data yang Anda buat dengan hati-hati untuk sesuatu yang lain".
in
memiliki beberapa kekhasan, dan dalam beberapa kasus seperti di mana konversi implisit mungkin diperlukan untuk membuat short Anda kompatibel denganin int
kompiler akan membuat int sementara, memperluas short Anda untuk itu, meneruskannya dengan referensi dan menyelesaikan. Ini bisa dilakukan karena Anda sudah menyatakan tidak akan mengacaukannya.Microsoft melakukan ini dengan
.TryParse
metode pada tipe numerik:Dengan menandai parameter saat
out
mereka secara aktif menyatakan di sini, "kami pasti akan mengubah nilai 98234957 yang dibuat dengan susah payah untuk sesuatu yang lain"Tentu saja, mereka agak harus, untuk hal-hal seperti parsing tipe nilai karena jika metode parse tidak diizinkan untuk menukar tipe nilai untuk sesuatu yang lain itu tidak akan bekerja dengan baik .. Tapi bayangkan ada beberapa metode fiktif di beberapa perpustakaan yang Anda buat:
Anda dapat melihat ini sebuah
out
, dan dengan demikian Anda dapat mengetahui bahwa jika Anda menghabiskan berjam-jam angka, menciptakan SomeClass yang sempurna:Ya itu buang-buang waktu, menghabiskan waktu berjam-jam untuk membuat kelas yang sempurna. Ini pasti akan dibuang dan digantikan oleh PoorlyNamedMethod
sumber
Bagi mereka yang mencari jawaban singkat.
sumber
Untuk mengilustrasikan banyak penjelasan yang sangat baik, saya mengembangkan aplikasi konsol berikut:
AppendWorld
: SalinanStringList
namaLiStri
dilewatkan. Pada awal metode, salinan ini merujuk pada daftar asli dan karenanya dapat digunakan untuk mengubah daftar ini. NantiLiStri
referensiList<string>
objek lain di dalam metode yang tidak mempengaruhi daftar asli.HalloWelt
:LiStriRef
adalah alias dari yang sudah diinisialisasiListStringRef
.List<string>
Objek yang dikirimkan digunakan untuk menginisialisasi yang baru, oleh karenaref
itu diperlukan.CiaoMondo
:LiStriOut
adalah aliasListStringOut
dan harus diinisialisasi.Jadi, jika suatu metode hanya memodifikasi objek yang direferensikan oleh variabel yang dikirimkan, kompiler tidak akan membiarkan Anda menggunakan
out
dan Anda tidak boleh menggunakanref
karena itu akan membingungkan bukan kompiler tetapi pembaca kode. Jika metode akan membuat argumen yang diteruskan referensi objek lain, gunakanref
untuk objek yang sudah diinisialisasi danout
untuk metode yang harus menginisialisasi objek baru untuk argumen yang diteruskan. Selain itu,ref
danout
berperilaku sama.sumber
Mereka hampir sama - satu-satunya perbedaan adalah bahwa variabel yang Anda lewati sebagai parameter keluar tidak perlu diinisialisasi, dan metode menggunakan parameter ref harus mengaturnya menjadi sesuatu.
Parameter ref untuk data yang dapat dimodifikasi, parameter out untuk data yang merupakan output tambahan untuk fungsi (mis. Int.TryParse) yang sudah menggunakan nilai balik untuk sesuatu.
sumber
Di bawah ini saya telah menunjukkan contoh menggunakan Ref dan out . Sekarang, Anda semua akan dibersihkan tentang ref dan keluar.
Dalam contoh yang disebutkan di bawah ketika saya berkomentar // myRefObj = new myClass {Name = "ref outside called !!"}; baris, akan mendapatkan kesalahan yang mengatakan "Penggunaan variabel lokal yang belum ditetapkan 'myRefObj'" , tetapi tidak ada kesalahan seperti keluar .
Tempat menggunakan Ref : ketika kita memanggil prosedur dengan parameter in dan parameter yang sama akan digunakan untuk menyimpan output dari proc itu.
Di mana harus menggunakan Keluar: ketika kita memanggil prosedur tanpa parameter dalam dan parameter yang sama akan digunakan untuk mengembalikan nilai dari proc itu. Perhatikan juga hasilnya
sumber
Anda dapat memeriksa kode ini akan menjelaskan Anda perbedaan lengkapnya ketika Anda menggunakan "ref" artinya Anda sudah menginisialisasi int / string
tetapi ketika Anda menggunakan "out" itu berfungsi dalam kedua kondisi di mana Anda menginisialisasi int / string atau tidak tetapi Anda harus menginisialisasi int / string dalam fungsi itu
sumber
Ref: Kata kunci ref digunakan untuk memberikan argumen sebagai referensi. Ini berarti bahwa ketika nilai parameter itu diubah dalam metode, itu akan tercermin dalam metode panggilan. Argumen yang dilewatkan menggunakan kata kunci ref harus diinisialisasi dalam metode panggilan sebelum diteruskan ke metode yang dipanggil.
Keluar: Kata kunci keluar juga digunakan untuk menyampaikan argumen seperti kata kunci ref, tetapi argumen tersebut dapat disampaikan tanpa memberikan nilai apa pun padanya. Argumen yang dilewatkan menggunakan kata kunci keluar harus diinisialisasi dalam metode yang dipanggil sebelum kembali ke metode panggilan.
Ref dan keluar dalam kelebihan metode
Baik ref dan out tidak dapat digunakan dalam metode overloading secara bersamaan. Namun, ref dan out diperlakukan secara berbeda pada saat run-time tetapi mereka diperlakukan sama pada waktu kompilasi (CLR tidak membedakan antara keduanya saat itu menciptakan IL untuk ref dan out).
sumber
Dari sudut pandang metode yang menerima parameter, perbedaan antara
ref
danout
adalah bahwa C # mensyaratkan bahwa metode harus menulis ke setiapout
parameter sebelum kembali, dan tidak boleh melakukan apa pun dengan parameter seperti itu, selain meneruskannya sebagaiout
parameter atau menulisnya , hingga telah dilewatkan sebagaiout
parameter ke metode lain atau ditulis secara langsung. Perhatikan bahwa beberapa bahasa lain tidak memaksakan persyaratan tersebut; metode virtual atau antarmuka yang dideklarasikan dalam C # denganout
parameter dapat diganti dalam bahasa lain yang tidak memaksakan pembatasan khusus pada parameter tersebut.Dari sudut pandang penelepon, C # dalam banyak keadaan akan menganggap ketika memanggil metode dengan
out
parameter akan menyebabkan variabel yang lewat ditulis tanpa dibaca terlebih dahulu. Asumsi ini mungkin tidak benar ketika memanggil metode yang ditulis dalam bahasa lain. Sebagai contoh:Jika
myDictionary
mengidentifikasiIDictionary<TKey,TValue>
implementasi yang ditulis dalam bahasa selain C #, meskipunMyStruct s = new MyStruct(myDictionary);
terlihat seperti tugas, itu berpotensi meninggalkans
tidak dimodifikasi.Perhatikan bahwa konstruktor yang ditulis dalam VB.NET, tidak seperti yang ada di C #, tidak membuat asumsi tentang apakah metode yang dipanggil akan mengubah
out
parameter apa pun , dan menghapus semua bidang tanpa syarat. Perilaku aneh yang disinggung di atas tidak akan terjadi dengan kode yang ditulis seluruhnya dalam VB atau seluruhnya dalam C #, tetapi dapat terjadi ketika kode yang ditulis dalam C # memanggil metode yang ditulis dalam VB.NET.sumber
Jika Anda ingin melewatkan parameter Anda sebagai ref, maka Anda harus menginisialisasi sebelum meneruskan parameter ke fungsi yang lain, kompiler itu sendiri akan menunjukkan kesalahan. Tetapi jika parameter keluar Anda tidak perlu menginisialisasi parameter objek sebelum meneruskannya ke metode. Anda dapat menginisialisasi objek dalam metode panggilan itu sendiri.
sumber
Perlu diingat bahwa parameter referensi yang dilewatkan di dalam fungsi langsung dikerjakan.
Sebagai contoh,
Ini akan menulis Dog, bukan Cat. Karenanya Anda harus langsung bekerja pada someObject.
sumber
Saya mungkin tidak begitu pandai dalam hal ini, tetapi tentu saja string (meskipun mereka secara teknis adalah tipe referensi dan hidup di tumpukan) dilewatkan oleh nilai, bukan referensi?
Ini sebabnya Anda perlu ref jika Anda ingin perubahan ada di luar ruang lingkup fungsi membuatnya, Anda tidak melewati referensi sebaliknya.
Sejauh yang saya tahu Anda hanya perlu ref untuk tipe / nilai dan string itu sendiri, karena string adalah tipe referensi yang berpura-pura tetapi bukan tipe nilai.
Tapi saya bisa benar-benar salah, saya baru.
sumber
Capitalize()
yang akan mengubah isi string menjadi huruf kapital. Jika Anda kemudian mengganti baris Andaa = "testing";
dengana.Capitalize();
, maka output Anda akan menjadi "HALO", bukan "Halo". Salah satu keuntungan dari tipe yang tidak dapat diubah adalah Anda dapat memberikan referensi dan tidak khawatir tentang kode lain yang mengubah nilainya.