Apa perbedaan antara ref dan out dalam runtime?

15

C # menyediakan refdan outkata kunci untuk membuat argumen untuk dilewatkan dengan referensi. Semantik keduanya sangat mirip. Satu-satunya perbedaan adalah dalam inisialisasi dari variabel yang digerakkan:

  • refmembutuhkan variabel yang diinisialisasi sebelum meneruskannya ke fungsi, outtidak.
  • outmembutuhkan variabel yang diinisialisasi di dalam fungsi, reftidak.

Kasus penggunaan kedua kata kunci ini juga hampir sama, dan penggunaannya yang terlalu sering saya percaya dianggap sebagai bau kode (meskipun ada kasus penggunaan yang valid seperti TryParsedan TryGetValuepola).

Karena ini dapatkah seseorang menjelaskan, mengapa ada dua alat yang sangat mirip dalam C # untuk kasus penggunaan yang begitu sempit?

Juga, pada MSDN , dinyatakan bahwa mereka memiliki perilaku run-time yang berbeda:

Meskipun kata kunci ref dan out menyebabkan perilaku run-time yang berbeda, mereka tidak dianggap sebagai bagian dari tanda tangan metode pada waktu kompilasi.

Bagaimana perilaku run-time mereka berbeda?

Kesimpulan

Kedua jawaban terlihat benar, terima kasih keduanya. Saya menerima jmoreno karena lebih eksplisit.

Gábor Angyal
sumber
Dugaan saya adalah keduanya diimplementasikan dengan cara yang sama seperti refdi C ++; ke pointer ke penunjuk objek (atau primitif) yang dimaksud. yaitu Int32.TryParse(myStr, out myInt)(C #) "dieksekusi" dengan cara yang sama seperti int32_tryParse(myStr, &myInt)(C); satu-satunya perbedaan adalah beberapa kendala yang diberlakukan oleh kompiler untuk mencegah bug. (Saya tidak akan memposting ini sebagai jawaban karena saya mungkin salah tentang bagaimana ini bekerja di belakang layar, tetapi ini adalah bagaimana saya membayangkan itu bekerja [karena masuk akal])
Cole Johnson

Jawaban:

10

Pada saat pertanyaan ini ditanyakan, artikel MSDN agak salah ( sejak itu telah diperbaiki ). Alih-alih "menyebabkan perilaku yang berbeda" itu harus "memerlukan perilaku yang berbeda".

Khususnya, kompiler memberlakukan persyaratan yang berbeda untuk dua kata kunci, meskipun mekanisme yang sama (IL) digunakan untuk mengaktifkan perilaku tersebut.

jmoreno
sumber
Jadi pemahaman saya adalah bahwa kompiler memeriksa hal-hal seperti mendereferensi outparameter atau tidak menetapkan refparameter, tetapi IL yang dipancarkannya hanyalah referensi. Apakah itu benar?
Andrew
Btw, orang mungkin mengatakan ini dari fakta bahwa out Tdan ref Tditerjemahkan secara identik dalam bahasa CLI lain seperti VB.Net atau C ++ / CLI.
ach
@AndrewPiliser: benar. Dengan asumsi bahwa kode mengkompilasi dengan cara baik (Anda tidak mencoba dereference ketika Anda tidak seharusnya), IL akan sama.
jmoreno
4

inilah artikel yang menarik tentang topik yang dapat menjawab pertanyaan Anda:

http://www.dotnetperls.com/ref

minat:

"Perbedaan antara ref dan out tidak dalam Common Language Runtime, tetapi dalam bahasa C # itu sendiri."

memperbarui:

pengembang senior di tempat kerja saya hanya menguatkan jawaban @ jmoreno, jadi pastikan Anda membacanya !.

Dan Beaulieu
sumber
Jadi jika saya mengerti dengan benar tidak ada perbedaan pada level IL, yang menyiratkan bahwa mereka tidak dapat memiliki perilaku run time yang berbeda. Ini akan bertentangan dengan apa yang dikatakan MSDN. Namun demikian, artikel yang menarik!
Gábor Angyal
@ GáborAngyal Anda tidak memiliki tautan ke artikel MSDN secara kebetulan, bukan? Mungkin menyenangkan untuk memilikinya di sini jika pendapat berbeda
Dan Beaulieu
Ini dia: msdn.microsoft.com/en-gb/library/t3c3bfhx.aspx , tapi itu sudah menjadi pertanyaan saya selama ini :)
Gábor Angyal
1
Apa stringhubungannya dengan semua ini?
Robert Harvey
Itu hanya parafrase dari artikel. Diperbarui untuk menghapus baris kedua ..
Dan Beaulieu
2

Runtime ? Sama sekali tidak ada. Anda tidak dapat membebani metode dengan parameter yang sama hanya berbeda dengan ref atau kata kunci.

Coba kompilasi ini dan Anda akan mendapatkan kesalahan kompilasi "Metode dengan tanda tangan yang sama sudah dinyatakan":

    private class MyClass
    {
        private void DoSomething(out int param)
        {
        }
        private void DoSomething(ref int param)
        {
        }
    }

Untuk menjawab pertanyaan ini: "... mengapa ada dua alat yang sangat mirip dalam C # untuk kasus penggunaan yang begitu sempit?"

Dari keterbacaan kode dan sudut pandang API ada perbedaan besar. Sebagai konsumen API saya tahu bahwa ketika "keluar" digunakan, API tidak bergantung pada parameter keluar. Sebagai pengembang API saya lebih suka "keluar" dan menggunakan "ref" hanya ketika benar-benar (Jarang!) Diperlukan. Lihat referensi ini untuk diskusi yang hebat:

/programming/1516876/when-to-use-ref-vs-out

Info pendukung: Saya mengkompilasi metode berikut dan membongkarnya. Saya menggunakan kata kunci ref dan out (dalam contoh ini), tetapi kode assembly tidak berubah kecuali untuk referensi alamat seperti yang saya harapkan:

    private class MyClass
    {
        internal void DoSomething(out int param)
        {
            param = 0;
        }
    }

00000000 push ebp
00000001 mov ebp, esp
00000003 push edi
00000004 push esi
00000005 push ebx
00000006 sub esp, 34h
00000009 xor eax, eax
0000000b mov dword ptr [ebp-10h], eax
0000000e mov dword ptr [ebp-
0000] mov dword ptr [ebp-3Ch], ecx
00000014 mov dword ptr [ebp-40h], edx
00000017 cmp dword ptr ds: [008B1710h], 0
0000001e je 00000025
00000020 call 6E6B601E
00000025 nop
param = 0;
00000026 mov eax, dword ptr [ebp-40h]
00000029 xor edx, edx
0000002b mov dword ptr [eax], edx
}
0000002d nop
0000002e lea esp, [ebp-0Ch]
00000031 pop ebx
00000032 pop esi
00000033 pop edi
00000034 pop ebp
00000035 ret

Apakah saya membaca perakitan dengan benar?

Shmoken
sumber
Apa yang Anda tulis sudah benar, tetapi harap dicatat bahwa itu bukan implikasi yang jelas bahwa tidak ada perbedaan runtime karena kelebihan ini tidak diperbolehkan.
Gábor Angyal
Ahh - poin bagus! Adakah yang bisa membongkar kode di atas dan membuktikan saya benar atau salah? Saya buruk membaca majelis, tetapi ingin tahu.
Shmoken
Oke - Saya membongkar kode menggunakan "keluar" dan "ref" dan saya tidak dapat melihat perbedaan untuk metode DoSomething saya:
Shmoken