Apa penggunaan "ref" untuk variabel tipe referensi dalam C #?

176

Saya mengerti bahwa jika saya meneruskan tipe-nilai ( int,, structdll.) Sebagai parameter (tanpa refkata kunci), salinan variabel tersebut diteruskan ke metode, tetapi jika saya menggunakan refkata kunci, referensi ke variabel tersebut diteruskan, bukan yang baru.

Tetapi dengan tipe referensi, seperti kelas, bahkan tanpa refkata kunci, referensi diteruskan ke metode, bukan salinan. Jadi apa gunanya refkata kunci dengan tipe referensi?


Ambil contoh:

var x = new Foo();

Apa perbedaan antara yang berikut ini?

void Bar(Foo y) {
    y.Name = "2";
}

dan

void Bar(ref Foo y) {
    y.Name = "2";
}
Andreas Grech
sumber

Jawaban:

154

Anda dapat mengubah foopoin apa yang digunakan y:

Foo foo = new Foo("1");

void Bar(ref Foo y)
{
    y = new Foo("2");
}

Bar(ref foo);
// foo.Name == "2"
pengguna7116
sumber
17
sehingga Anda pada dasarnya mendapatkan referensi dengan aslinya referensi
lhahne
2
Anda dapat mengubah apa yang referensi asli 'rujuk', jadi ya.
user7116
1
Chris, penjelasanmu bagus; Terima kasih telah membantu saya memahami konsep ini.
Andreas Grech
4
Jadi menggunakan 'ref' pada objek seperti menggunakan pointer ganda di C ++?
Tom Hazel
1
@ TomHazel: -ish , asalkan Anda menggunakan pointer "ganda" dalam C ++ untuk mengubah apa yang ditunjuk oleh pointer.
user7116
29

Ada kasus di mana Anda ingin memodifikasi referensi yang sebenarnya dan bukan objek yang diarahkan:

void Swap<T>(ref T x, ref T y) {
    T t = x;
    x = y;
    y = t;
}

var test = new[] { "0", "1" };
Swap(ref test[0], ref test[1]);
Mehrdad Afshari
sumber
21

Jon Skeet menulis artikel yang bagus tentang melewati parameter dalam C #. Ini merinci dengan jelas perilaku yang tepat dan penggunaan parameter lewat oleh nilai, dengan referensi ( ref), dan oleh output ( out).

Berikut ini kutipan penting dari halaman tersebut terkait dengan refparameter:

Parameter referensi tidak melewatkan nilai variabel yang digunakan dalam doa anggota fungsi - mereka menggunakan variabel itu sendiri. Daripada membuat lokasi penyimpanan baru untuk variabel dalam deklarasi anggota fungsi, lokasi penyimpanan yang sama digunakan, sehingga nilai variabel dalam anggota fungsi dan nilai parameter referensi akan selalu sama. Parameter referensi memerlukan pengubah ref sebagai bagian dari deklarasi dan doa - itu berarti selalu jelas ketika Anda melewati sesuatu dengan referensi.

Noldorin
sumber
11
Saya suka analogi mengoper tali anjing Anda ke teman karena melewati referensi dengan nilai ... itu rusak dengan cepat, karena saya pikir Anda mungkin akan melihat jika teman Anda menukar shitzu Anda dengan tukang kunci sebelum dia mengembalikan Anda the leash ;-)
corlettk
16

Dijelaskan dengan sangat baik di sini: http://msdn.microsoft.com/en-us/library/s6938f28.aspx

Abstrak dari artikel:

Variabel tipe referensi tidak berisi datanya secara langsung; itu berisi referensi ke datanya. Ketika Anda melewati parameter tipe referensi dengan nilai, dimungkinkan untuk mengubah data yang ditunjuk oleh referensi, seperti nilai anggota kelas. Namun, Anda tidak dapat mengubah nilai referensi itu sendiri; yaitu, Anda tidak dapat menggunakan referensi yang sama untuk mengalokasikan memori untuk kelas baru dan tetap ada di luar blok. Untuk melakukan itu, berikan parameter menggunakan kata kunci ref atau out.

himanshupareek66
sumber
4
Penjelasannya memang sangat bagus. Namun, jawaban hanya tautan tidak disarankan pada SO. Saya menambahkan ringkasan dari artikel tersebut, sebagai kenyamanan bagi para pembaca di sini.
Marcel
10

Saat Anda melewati jenis referensi dengan kata kunci ref, Anda meneruskan referensi dengan referensi, dan metode yang Anda panggil dapat menetapkan nilai baru ke parameter. Perubahan itu akan menyebar ke lingkup panggilan. Tanpa ref, referensi dilewatkan oleh nilai, dan ini tidak terjadi.

C # juga memiliki kata kunci 'keluar' yang sangat mirip dengan ref, kecuali bahwa dengan 'ref', argumen harus diinisialisasi sebelum memanggil metode, dan dengan 'out' Anda harus memberikan nilai dalam metode penerimaan.

Rytmis
sumber
5

Ini memungkinkan Anda untuk memodifikasi referensi yang diteruskan. Mis

void Bar()
{
    var y = new Foo();
    Baz(ref y);
}

void Baz(ref Foo y)
{
    y.Name = "2";

    // Overwrite the reference
    y = new Foo();
}

Anda juga dapat menggunakan keluar jika Anda tidak peduli tentang referensi disahkan pada:

void Bar()
{
    var y = new Foo();
    Baz(out y);
}

void Baz(out Foo y)
{
    // Return a new reference
    y = new Foo();
}
Hans Van Slooten
sumber
4

Kumpulan kode lain

class O
{
    public int prop = 0;
}

class Program
{
    static void Main(string[] args)
    {
        O o1 = new O();
        o1.prop = 1;

        O o2 = new O();
        o2.prop = 2;

        o1modifier(o1);
        o2modifier(ref o2);

        Console.WriteLine("1 : " + o1.prop.ToString());
        Console.WriteLine("2 : " + o2.prop.ToString());
        Console.ReadLine();
    }

    static void o1modifier(O o)
    {
        o = new O();
        o.prop = 3;
    }

    static void o2modifier(ref O o)
    {
        o = new O();
        o.prop = 4;
    }
}
Svend
sumber
3

Selain jawaban yang ada:

Saat Anda meminta perbedaan kedua metode: Tidak ada varian co (ntra) saat menggunakan refatau out:

class Foo { }
class FooBar : Foo { }

static void Bar(Foo foo) { }
static void Bar(ref Foo foo) { foo = new Foo(); }

void Main()
{
    Foo foo = null;
    Bar(foo);           // OK
    Bar(ref foo);       // OK

    FooBar fooBar = null;
    Bar(fooBar);        // OK (covariance)
    Bar(ref fooBar);    // compile time error
}
springy76
sumber
1

Parameter dalam metode tampaknya selalu melewati salinan, pertanyaannya adalah salinan apa. Salinan dilakukan oleh copy constructor untuk suatu objek dan karena semua variabel adalah Object di C #, saya percaya ini adalah kasus untuk semuanya. Variabel (objek) seperti orang yang tinggal di beberapa alamat. Kami dapat mengubah orang-orang yang tinggal di alamat-alamat itu atau kami dapat membuat lebih banyak referensi untuk orang-orang yang tinggal di alamat-alamat itu dalam buku telepon (membuat salinan yang dangkal). Jadi, lebih dari satu pengidentifikasi dapat merujuk ke alamat yang sama. Tipe referensi menginginkan lebih banyak ruang, jadi tidak seperti tipe nilai yang secara langsung dihubungkan oleh panah ke pengidentifikasi mereka di stack, mereka memiliki nilai untuk alamat lain di heap (ruang yang lebih besar untuk dihuni). Ruang ini perlu diambil dari tumpukan.

Jenis nilai: Indentifier (berisi nilai = alamat nilai tumpukan) ----> Nilai jenis nilai

Jenis referensi: Identifier (berisi nilai = alamat nilai tumpukan) ----> (berisi nilai = alamat nilai tumpukan) ----> Nilai tumpukan (paling sering berisi alamat ke nilai lain), bayangkan lebih banyak panah menempel di berbagai arah ke Array [0], Array [1], array [2]

Satu-satunya cara untuk mengubah nilai adalah dengan mengikuti panah. Jika satu panah hilang / berubah dengan cara nilainya tidak dapat dijangkau.

Petar Drianov
sumber
-1

Variabel Referensi membawa alamat dari satu tempat ke tempat lain sehingga setiap pembaruan pada mereka di tempat mana pun akan mencerminkan semua tempat LALU apa gunanya REF. Variabel referensi (405) baik sampai tidak ada memori baru yang dialokasikan ke variabel referensi yang diteruskan dalam metode.

Setelah alokasi memori baru (410) maka perubahan nilai pada objek ini (408) tidak akan mencerminkan di mana-mana. Untuk ref ini datang. Ref adalah referensi referensi sehingga setiap kali memori baru mengalokasikannya akan tahu karena menunjuk ke lokasi itu sehingga nilainya dapat dibagikan oleh setiap orang. Anda dapat melihat gambar untuk lebih jelas.

Ref dalam Variabel Referensi

srbh
sumber