Apa perbedaan antara kata kunci 'ref' dan 'out'?

892

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?

TK.
sumber
69
Anda: Saya perlu meneruskan objek agar dapat dimodifikasi. Sepertinya MyClassakan menjadi classtipe, yaitu tipe referensi. Dalam hal itu, objek yang Anda lewati dapat dimodifikasi oleh myFunctiongenap tanpa ref/ outkata kunci. myFunctionakan menerima referensi baru yang menunjuk ke objek yang sama , dan dapat memodifikasi objek yang sama sebanyak yang diinginkan. Perbedaan refkata kunci akan membuat, akan myFunctionmenerima referensi yang sama untuk objek yang sama. Itu akan menjadi penting hanya jika myFunctionmengubah referensi untuk menunjuk ke objek lain .
Jeppe Stig Nielsen
3
Saya bingung dengan jumlah jawaban yang membingungkan di sini, ketika @ AnthonyKolesov cukup sempurna.
o0 '.
Mendeklarasikan metode keluar berguna ketika Anda ingin metode mengembalikan beberapa nilai. Satu argumen dapat ditugaskan ke null. Ini memungkinkan metode untuk mengembalikan nilai secara opsional.
Yevgraf Andreyevich Zhivago
Di sini dijelaskan dengan Contoh Ini lebih mudah dimengerti :) dotnet-tricks.com/Tutorial/csharp/…
Prageeth godage
2
Komentar @ JeppeStigNielsen adalah, secara teknis, (hanya) jawaban yang benar untuk pertanyaan aktual OP. Untuk meneruskan objek ke metode sehingga metode dapat memodifikasi objek , cukup kirimkan objek (referensi ke) ke metode dengan nilai. Mengubah objek dalam metode melalui argumen objek memodifikasi objek asli , meskipun metode tersebut berisi variabel terpisah sendiri (yang mereferensikan objek yang sama).
David R Tribble

Jawaban:

1162

refmemberitahu kompiler bahwa objek diinisialisasi sebelum memasuki fungsi, sementara outmemberitahu kompiler bahwa objek akan diinisialisasi di dalam fungsi.

Jadi, sementara refdua arah, outhanya keluar.

Rune Grimstad
sumber
270
Hal keren lain yang khusus untuk keluar adalah fungsi tersebut harus menetapkan parameter keluar. Tidak diizinkan membiarkannya tidak ditetapkan.
Daniel Earwicker
7
apakah 'ref' hanya berlaku untuk tipe nilai? Karena tipe referensi selalu diberikan oleh ref.
salah
3
Iya. Jenis nilai termasuk struct
Rune Grimstad
17
@ salah: Tidak, ref tidak hanya berlaku untuk tipe nilai. ref / out seperti pointer di C / C ++, mereka berurusan dengan lokasi memori objek (tidak langsung dalam C #), bukan objek langsung.
thr
52
@ salah: Secara berlawanan, tipe referensi selalu dilewati oleh nilai dalam C #, kecuali jika Anda menggunakan penspesifikasi ref. Jika Anda mengatur myval = somenewval, efeknya hanya dalam lingkup fungsi itu. Kata kunci ref akan memungkinkan Anda untuk mengubah myval untuk menunjuk ke somenewval.
JasonTrue
535

The refberarti modifikator yang:

  1. Nilai sudah diatur dan
  2. Metode ini dapat membaca dan memodifikasinya.

The outberarti modifikator yang:

  1. Nilai tidak ditetapkan dan tidak dapat dibaca oleh metode sampai nilai ditetapkan.
  2. Metode harus mengaturnya sebelum kembali.
Anton Kolesov
sumber
30
Jawaban ini paling jelas dan ringkas menjelaskan batasan yang diberlakukan oleh kompiler ketika menggunakan kata kunci keluar sebagai lawan dari kata kunci ref.
Apprentice Dr. Wily
5
Dari MSDN: Parameter ref harus diinisialisasi sebelum digunakan, sedangkan parameter keluar tidak harus diinisialisasi secara eksplisit sebelum diteruskan dan nilai sebelumnya diabaikan.
Shiva Kumar
1
Dengan 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?
Panzercrisis
3
Panzercrisis, untuk "keluar", metode yang disebut dapat membaca jika sudah ditetapkan. tetapi harus mengaturnya lagi.
robert jebakumar2
146

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.

Michael Blackburn
sumber
54
ref Dom akan menulis laporan dengan pensil sehingga Peter dapat memodifikasinya
Deebster
6
@ Monster Anda tahu, metafora itu tidak pernah melakukan apa pun pada Anda, mengapa Anda harus menyiksanya begitu? ;)
Michael Blackburn
21
menghibur namun mendidik, stackoverflow membutuhkan lebih banyak posting seperti ini
Frank Visaggio
2
Kalau-kalau ada yang menemukan jawaban ini hanya setengah-lucu, silakan tonton film "Office Space".
displayName
dan bos Dom dan Peters akan bersikap seperti Dom (sebagai argumen), memaksa keduanya untuk bekerja mencetaknya lagi sampai Peter menyerahkan Domd cetakannya
Patrick Artner
57

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:

List<string> someobject = new List<string>()

Saat Anda baru memulai suatu objek , dua bagian dibuat:

  1. Blok memori yang menampung data beberapa objek .
  2. Referensi (penunjuk) ke blok data itu.

Sekarang ketika Anda mengirim objek ke suatu metode tanpa ref COPIES itu adalah referensi pointer, BUKAN data. Jadi sekarang Anda memiliki ini:

(outside method) reference1 => someobject
(inside method)  reference2 => someobject

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.

 (inside method)  reference2.Add("SomeString");
 (outside method) reference1[0] == "SomeString"   //this is true

Jika Anda membatalkan referensi2 atau mengarahkannya ke data baru, itu tidak akan mempengaruhi reference1 atau data reference1 menunjuk ke.

(inside method) reference2 = new List<string>();
(outside method) reference1 != null; reference1[0] == "SomeString" //this is true

The references are now pointing like this:
reference2 => new List<string>()
reference1 => someobject

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:

(outside method) reference1 => someobject;
(inside method)  reference1 => someobject;

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.

 (inside method)  reference1 = null;
 (outside method) reference1 == null;  //true

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.

 (inside method)  reference1 = new List<string>();
 (outside method) reference1.Count == 0; //this is true
James Roland
sumber
Maksud Anda setelah semua (dalam kasus ref) hanya ada satu referensi untuk data tetapi dua alias untuk itu. Baik?
Sadiq
3
Terpilih untuk penjelasan yang jelas. Tapi saya pikir ini tidak menjawab pertanyaan, karena tidak menjelaskan perbedaan antara refdan outparameter.
Joyce Babu
1
Luar biasa. dapatkah Anda menjelaskan hal yang sama dengan outkata kunci?
Asif Mushtaq
28

ref masuk dan keluar .

Anda harus menggunakan outpreferensi di mana pun itu mencukupi untuk kebutuhan Anda.

Ruben Bartelink
sumber
tidak cukup, sebagai jawaban yang diterima ref jika arah dan berguna mengabaikan nilai-nilai jika tidak pingsan.
kenny
@kenny: Bisakah Anda mengklarifikasi sedikit tolong - yaitu, kata-kata mana yang akan Anda ubah untuk mempertahankan semangat jawaban tetapi menghapus ketidaktepatan yang Anda persepsikan? Jawaban saya bukan tebakan gila dari seorang pemula, tetapi tergesa-gesa (terseness, kesalahan ketik) dalam komentar Anda tampaknya menganggap itu. Tujuannya adalah untuk memberikan cara berpikir tentang perbedaan dengan jumlah kata yang paling sedikit.
Ruben Bartelink
(BTW Saya terbiasa dengan tipe nilai, tipe referensi, meneruskan dengan referensi, meneruskan dengan nilai, COM dan C ++ jika Anda merasa berguna untuk membuat referensi ke konsep-konsep tersebut dalam klarifikasi Anda)
Ruben Bartelink
1
Referensi objek diteruskan oleh nilai (kecuali saat menggunakan kata kunci "ref" atau "out"). Pikirkan benda sebagai nomor ID. Jika variabel kelas menyimpan "Objek # 1943" dan seseorang meneruskan variabel itu dengan nilai ke rutin, maka rutin itu dapat membuat perubahan ke Objek # 1943, tetapi itu tidak dapat membuat titik variabel ke apa pun selain "Objek # 1943". Jika variabel dilewatkan dengan referensi, rutin bisa membuat titik variabel tahan "Objek # 5441".
supercat
1
@supercat: Saya suka penjelasan Anda tentang ref vs val (dan anaologi tindak lanjut ini). Saya pikir kenny sebenarnya tidak perlu semua ini dijelaskan kepadanya, (relatif) membingungkan seperti komentarnya. Saya berharap kita semua bisa menghapus komentar sialan ini karena mereka hanya membingungkan semua orang. Penyebab utama dari semua omong kosong ini adalah kenny yang salah membaca jawaban saya dan belum menunjukkan satu kata pun yang harus ditambahkan / dihapus / diganti. Tidak satu pun dari kami bertiga telah belajar sesuatu dari diskusi yang belum kami ketahui dan jawaban lainnya memiliki jumlah upvotes yang menggelikan.
Ruben Bartelink
18

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.

  1. Anda tidak perlu menginisialisasi nilai dalam fungsi panggilan.
  2. Anda harus menetapkan nilai dalam fungsi yang dipanggil, jika tidak kompiler akan melaporkan kesalahan.

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.

  1. Anda perlu menginisialisasi variabel sebelum memanggil fungsi.
  2. Ini tidak wajib untuk menetapkan nilai apa pun ke parameter ref dalam metode ini. Jika Anda tidak mengubah nilainya, apa perlunya menandainya sebagai "ref"?
Nazmul Hasan
sumber
"Dalam C #, metode hanya dapat mengembalikan satu nilai. Jika Anda ingin mengembalikan lebih dari satu nilai, Anda dapat menggunakan kata kunci keluar." Kita juga dapat menggunakan "ref" untuk mengembalikan nilai. Jadi kita bisa menggunakan ref dan keluar jika kita ingin mengembalikan beberapa nilai dari suatu metode?
Ned
1
Di c # 7 Anda dapat mengembalikan beberapa nilai dengan ValueTuples.
Iman Bahrampour
13

Memperluas Anjing, contoh Kucing. Metode kedua dengan ref mengubah objek yang dirujuk oleh pemanggil. Karenanya "Kucing" !!!

    public static void Foo()
    {
        MyClass myObject = new MyClass();
        myObject.Name = "Dog";
        Bar(myObject);
        Console.WriteLine(myObject.Name); // Writes "Dog". 
        Bar(ref myObject);
        Console.WriteLine(myObject.Name); // Writes "Cat". 
    }

    public static void Bar(MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

    public static void Bar(ref MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }
BBB
sumber
8

Karena Anda memasukkan tipe referensi (kelas) tidak perlu digunakan refkarena per default hanya referensi ke objek aktual yang dilewati dan oleh karena itu Anda selalu mengubah objek di belakang referensi.

Contoh:

public void Foo()
{
    MyClass myObject = new MyClass();
    myObject.Name = "Dog";
    Bar(myObject);
    Console.WriteLine(myObject.Name); // Writes "Cat".
}

public void Bar(MyClass someObject)
{
    someObject.Name = "Cat";
}

Selama Anda lulus di kelas Anda tidak harus menggunakan refjika Anda ingin mengubah objek di dalam metode Anda.

Albic
sumber
5
Ini hanya berfungsi jika tidak ada objek baru yang dibuat dan dikembalikan. Ketika objek baru dibuat, referensi ke objek lama akan hilang.
etsuba
8
Ini salah - coba yang berikut ini: tambahkan someObject = nulluntuk Barmengakhiri eksekusi. Kode Anda akan berjalan dengan baik karena hanya Barreferensi ke instance yang dibatalkan. Sekarang ubah Barmenjadi Bar(ref MyClass someObject)dan jalankan lagi - Anda akan mendapatkan referensi NullReferenceExceptionkarena Foountuk instance telah dibatalkan juga.
Keith
8

refdan outberperilaku sama kecuali mengikuti perbedaan.

  • refvariabel harus diinisialisasi sebelum digunakan. outvariabel dapat digunakan tanpa penugasan
  • outparameter harus diperlakukan sebagai nilai yang tidak ditetapkan oleh fungsi yang menggunakannya. Jadi, kita bisa menggunakan outparameter yang diinisialisasi dalam kode panggilan, tetapi nilainya akan hilang ketika fungsi dijalankan.
pengguna gmail
sumber
8

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

2upmedia
sumber
6

"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

string s = "Able";

maka s adalah referensi ke kelas string yang berisi teks "Mampu"! Tugas lain ke variabel yang sama via

s = "Baker";

tidak mengubah string asli tetapi hanya membuat instance baru dan mari kita tunjukkan ke instance itu!

Anda dapat mencobanya dengan contoh kode kecil berikut:

string s = "Able";
string s2 = s;
s = "Baker";
Console.WriteLine(s2);

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)

mmmmmmmm
sumber
1
Persis. Jadi tidak sepenuhnya benar untuk mengatakan "Karena Anda memasukkan tipe referensi (kelas) tidak perlu menggunakan ref".
Paul Mitchell
Secara teori itu benar untuk mengatakannya karena ia menulis "sehingga dapat dimodifikasi" yang tidak mungkin dilakukan pada string. Tetapi karena objek abadi "ref" dan "out" sangat berguna juga untuk tipe referensi! (.Net mengandung banyak kelas yang tidak dapat diubah!)
mmmmmmmm
Ya kau benar. Saya tidak memikirkan objek yang tidak dapat diubah seperti string karena sebagian besar objek dapat berubah.
Albic
1
Yah, ini adalah jawaban yang membingungkan untuk dilihat di LQP, untuk memastikan; tidak ada yang salah dengan itu kecuali bahwa itu tampaknya merupakan tanggapan yang panjang dan menyeluruh terhadap komentar lain (karena pertanyaan awal menyebutkan Able dan Baker tidak ada dalam revisi), seolah-olah ini adalah forum. Saya kira itu belum benar-benar beres saat kembali.
Nathan Tuggy
6

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.

Farhan S.
sumber
5

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:

using System;

namespace CalculatorApplication
{
   class NumberManipulator
   {
      public void getValue(out int x )
      {
         int temp = 5;
         x = temp;
      }

      static void Main(string[] args)
      {
         NumberManipulator n = new NumberManipulator();
         /* local variable definition */
         int a = 100;

         Console.WriteLine("Before method call, value of a : {0}", a);

         /* calling a function to get the value */
         n.getValue(out a);

         Console.WriteLine("After method call, value of a : {0}", a);
         Console.ReadLine();

      }
   }
}

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:

using System;
namespace CalculatorApplication
{
   class NumberManipulator
   {
      public void swap(ref int x, ref int y)
      {
         int temp;

         temp = x; /* save the value of x */
         x = y;   /* put y into x */
         y = temp; /* put temp into y */
       }

      static void Main(string[] args)
      {
         NumberManipulator n = new NumberManipulator();
         /* local variable definition */
         int a = 100;
         int b = 200;

         Console.WriteLine("Before swap, value of a : {0}", a);
         Console.WriteLine("Before swap, value of b : {0}", b);

         /* calling a function to swap the values */
         n.swap(ref a, ref b);

         Console.WriteLine("After swap, value of a : {0}", a);
         Console.WriteLine("After swap, value of b : {0}", b);

         Console.ReadLine();

      }
   }
}
Faisal Naseer
sumber
4

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

        double nbr = 6; // if not initialized we get error
        double dd = doit.square(ref nbr);

        double Half_nbr ; // fine as passed by out, but inside the calling  method you initialize it
        doit.math_routines(nbr, out Half_nbr);
Putar Roda
sumber
1
Anda dapat mendeklarasikan inline variabel: out double Half_nbr.
Sebastian Hofmann
4

Waktu Penulisan:

(1) Kami membuat metode panggilan Main()

(2) itu membuat objek Daftar (yang merupakan objek tipe referensi) dan menyimpannya dalam variabel myList.

public sealed class Program 
{
    public static Main() 
    {
        List<int> myList = new List<int>();

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 myParamListke metode yang dipanggil modifyMyListdan menetapkan objek Daftar baru untuk itu

List<int> myList = new List<int>();

List<int> newList = ModifyMyList(myList)

public List<int> ModifyMyList(List<int> myParamList){
     myParamList = new List<int>();
     return myParamList;
}

Selama 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 myListpointer 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.

supi
sumber
3

Selain memungkinkan Anda untuk menetapkan kembali variabel orang lain ke instance kelas yang berbeda, mengembalikan beberapa nilai dll, menggunakan refatau outmembiarkan orang lain tahu apa yang Anda butuhkan dari mereka dan apa yang ingin Anda lakukan dengan variabel yang mereka sediakan

  • Anda tidak perlu ref atau outjika semua yang akan Anda lakukan adalah memodifikasi hal-hal di dalam MyClassinstance yang diteruskan dalam argumen someClass.

    • Metode panggilan akan melihat perubahan seperti someClass.Message = "Hello World"apakah Anda menggunakan ref, outatau tidak sama sekali
    • Menulis someClass = new MyClass()di dalam myFunction(someClass)menukar objek yang dilihat oleh someClassdalam lingkup myFunctionmetode saja. Metode panggilan masih tahu tentang MyClassinstance asli yang dibuat dan diteruskan ke metode Anda
  • Anda perlu ref atau outjika Anda berencana untuk menukar someClassobjek yang sama sekali baru dan ingin metode panggilan untuk melihat perubahan Anda

    • Menulis someClass = new MyClass()di dalam myFunction(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 refmembuat 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 outmembuat 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 selesai

Omong-omong, di C # 7.2 ada inpengubah juga

Dan 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". inmemiliki beberapa kekhasan, dan dalam beberapa kasus seperti di mana konversi implisit mungkin diperlukan untuk membuat short Anda kompatibel dengan in intkompiler 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 .TryParsemetode pada tipe numerik:

int i = 98234957;
bool success = int.TryParse("123", out i);

Dengan menandai parameter saat outmereka 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:

public void PoorlyNamedMethod(out SomeClass x)

Anda dapat melihat ini sebuah out, dan dengan demikian Anda dapat mengetahui bahwa jika Anda menghabiskan berjam-jam angka, menciptakan SomeClass yang sempurna:

SomeClass x = SpendHoursMakingMeAPerfectSomeClass();
//now give it to the library
PoorlyNamedMethod(out x);

Ya itu buang-buang waktu, menghabiskan waktu berjam-jam untuk membuat kelas yang sempurna. Ini pasti akan dibuang dan digantikan oleh PoorlyNamedMethod

Caius Jard
sumber
3

Bagi mereka yang mencari jawaban singkat.

Keduanya refdan outkata kunci digunakan untuk pass-by- reference.


Variabel refkata kunci harus memiliki nilai atau harus merujuk ke suatu objek atau null sebelum lewat.


Tidak seperti ref, variabel outkata kunci harus memiliki nilai atau harus merujuk ke suatu objek atau null setelah lewat serta tidak perlu memiliki nilai atau merujuk ke objek sebelum melewati.

snr
sumber
2

Untuk mengilustrasikan banyak penjelasan yang sangat baik, saya mengembangkan aplikasi konsol berikut:

using System;
using System.Collections.Generic;

namespace CSharpDemos
{
  class Program
  {
    static void Main(string[] args)
    {
      List<string> StringList = new List<string> { "Hello" };
      List<string> StringListRef = new List<string> { "Hallo" };

      AppendWorld(StringList);
      Console.WriteLine(StringList[0] + StringList[1]);

      HalloWelt(ref StringListRef);
      Console.WriteLine(StringListRef[0] + StringListRef[1]);

      CiaoMondo(out List<string> StringListOut);
      Console.WriteLine(StringListOut[0] + StringListOut[1]);
    }

    static void AppendWorld(List<string> LiStri)
    {
      LiStri.Add(" World!");
      LiStri = new List<string> { "¡Hola", " Mundo!" };
      Console.WriteLine(LiStri[0] + LiStri[1]);
    }

    static void HalloWelt(ref List<string> LiStriRef)
     { LiStriRef = new List<string> { LiStriRef[0], " Welt!" }; }

    static void CiaoMondo(out List<string> LiStriOut)
     { LiStriOut = new List<string> { "Ciao", " Mondo!" }; }
   }
}
/*Output:
¡Hola Mundo!
Hello World!
Hallo Welt!
Ciao Mondo!
*/
  • AppendWorld: Salinan StringListnama LiStridilewatkan. Pada awal metode, salinan ini merujuk pada daftar asli dan karenanya dapat digunakan untuk mengubah daftar ini. Nanti LiStrireferensi List<string>objek lain di dalam metode yang tidak mempengaruhi daftar asli.

  • HalloWelt: LiStriRefadalah alias dari yang sudah diinisialisasi ListStringRef. List<string>Objek yang dikirimkan digunakan untuk menginisialisasi yang baru, oleh karena refitu diperlukan.

  • CiaoMondo: LiStriOutadalah alias ListStringOutdan harus diinisialisasi.

Jadi, jika suatu metode hanya memodifikasi objek yang direferensikan oleh variabel yang dikirimkan, kompiler tidak akan membiarkan Anda menggunakan outdan Anda tidak boleh menggunakan refkarena itu akan membingungkan bukan kompiler tetapi pembaca kode. Jika metode akan membuat argumen yang diteruskan referensi objek lain, gunakan refuntuk objek yang sudah diinisialisasi dan outuntuk metode yang harus menginisialisasi objek baru untuk argumen yang diteruskan. Selain itu, refdan outberperilaku sama.

Dietrich Baumgarten
sumber
1

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.

int x;    Foo(out x); // OK 
int y;    Foo(ref y); // Error

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.

Talha Khan
sumber
1

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

public partial class refAndOutUse : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        myClass myRefObj;
        myRefObj = new myClass { Name = "ref outside called!!  <br/>" };
        myRefFunction(ref myRefObj);
        Response.Write(myRefObj.Name); //ref inside function

        myClass myOutObj;
        myOutFunction(out myOutObj);
        Response.Write(myOutObj.Name); //out inside function
    }

    void myRefFunction(ref myClass refObj)
    {
        refObj.Name = "ref inside function <br/>";
        Response.Write(refObj.Name); //ref inside function
    }
    void myOutFunction(out myClass outObj)
    {
        outObj = new myClass { Name = "out inside function <br/>" }; 
        Response.Write(outObj.Name); //out inside function
    }
}

public class myClass
{
    public string Name { get; set; }
} 
Ankur Bhutani
sumber
1
 public static void Main(string[] args)
    {
        //int a=10;
        //change(ref a);
        //Console.WriteLine(a);
        // Console.Read();

        int b;
        change2(out b);
        Console.WriteLine(b);
        Console.Read();
    }
    // static void change(ref int a)
    //{
    //    a = 20;
    //}

     static void change2(out int b)
     {
         b = 20;
     }

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

Haris Zia
sumber
1

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.

public class Example
{
 public static void Main() 
 {
 int val1 = 0; //must be initialized 
 int val2; //optional

 Example1(ref val1);
 Console.WriteLine(val1); 

 Example2(out val2);
 Console.WriteLine(val2); 
 }

 static void Example1(ref int value) 
 {
 value = 1;
 }
 static void Example2(out int value) 
 {
 value = 2; 
 }
}

/* Output     1     2     

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).

Dejan Ciev
sumber
0

Dari sudut pandang metode yang menerima parameter, perbedaan antara refdan outadalah bahwa C # mensyaratkan bahwa metode harus menulis ke setiap outparameter sebelum kembali, dan tidak boleh melakukan apa pun dengan parameter seperti itu, selain meneruskannya sebagai outparameter atau menulisnya , hingga telah dilewatkan sebagai outparameter ke metode lain atau ditulis secara langsung. Perhatikan bahwa beberapa bahasa lain tidak memaksakan persyaratan tersebut; metode virtual atau antarmuka yang dideklarasikan dalam C # dengan outparameter 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 outparameter 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:

struct MyStruct
{
   ...
   myStruct(IDictionary<int, MyStruct> d)
   {
     d.TryGetValue(23, out this);
   }
}

Jika myDictionarymengidentifikasi IDictionary<TKey,TValue>implementasi yang ditulis dalam bahasa selain C #, meskipun MyStruct s = new MyStruct(myDictionary);terlihat seperti tugas, itu berpotensi meninggalkan stidak 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 outparameter 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.

supercat
sumber
0

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.

Rakeshkumar Das
sumber
-3

Perlu diingat bahwa parameter referensi yang dilewatkan di dalam fungsi langsung dikerjakan.

Sebagai contoh,

    public class MyClass
    {
        public string Name { get; set; }
    }

    public void Foo()
    {
        MyClass myObject = new MyClass();
        myObject.Name = "Dog";
        Bar(myObject);
        Console.WriteLine(myObject.Name); // Writes "Dog".
    }

    public void Bar(MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

Ini akan menulis Dog, bukan Cat. Karenanya Anda harus langsung bekerja pada someObject.

Mangesh Pimpalkar
sumber
6
Meskipun semua yang ada di sini benar, namun tidak menjelaskan perbedaan antara nilai berdasarkan referensi atau keluar. Paling-paling setengah menjelaskan perbedaan antara referensi dan nilai / tipe tidak berubah.
Conrad Frix
Jika Anda ingin kode itu untuk menulis cat, sampaikan objek itu bersama dengan kunci 'ref' seperti ini: public static void Bar (ref MyClass someObject), Bar (ref myObject);
Daniel Botero Correa
-4

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?

        string a = "Hello";

        string b = "goodbye";

        b = a; //attempt to make b point to a, won't work.

        a = "testing";

        Console.WriteLine(b); //this will produce "hello", NOT "testing"!!!!

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.

Edwin
sumber
5
Selamat datang di Stack Overflow, Edwin. String dilewatkan dengan referensi, sama seperti objek lain, sejauh yang saya tahu. Anda mungkin bingung karena string adalah objek yang tidak dapat diubah, jadi tidak jelas apakah string itu dilewatkan dengan referensi. Bayangkan string yang memiliki metode yang disebut Capitalize()yang akan mengubah isi string menjadi huruf kapital. Jika Anda kemudian mengganti baris Anda a = "testing";dengan a.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.
Don Kirkby
2
Ada tiga tipe dasar semantik yang dapat diekspos oleh suatu tipe: semantik referensi yang dapat berubah, semantik nilai yang dapat berubah, dan semantik yang tidak dapat diubah. Pertimbangkan variabel x dan y dari tipe T, yang memiliki bidang atau properti m, dan anggap x disalin ke y. Jika T memiliki semantik referensi, perubahan ke xm akan diamati oleh ym. Jika T memiliki semantik nilai, seseorang dapat mengubah xm tanpa memengaruhi ym. Jika T memiliki semantik yang tidak berubah, baik xm maupun ym tidak akan pernah berubah. Semantik yang tidak dapat diubah dapat disimulasikan oleh objek referensi atau nilai. String adalah objek referensi yang tidak berubah.
supercat