ข้อแตกต่างระหว่าง ref และ out ใน runtime คืออะไร?


15

C # จัดให้มีrefและoutคำหลักเพื่อสร้างอาร์กิวเมนต์ที่จะส่งผ่านโดยการอ้างอิง ความหมายของทั้งสองนั้นคล้ายกันมาก ข้อแตกต่างเพียงอย่างเดียวคือการเริ่มต้นของตัวแปร flaged:

  • refต้องการตัวแปรที่จะเริ่มต้นก่อนที่จะส่งผ่านไปยังฟังก์ชั่นoutไม่ได้
  • outต้องการตัวแปรที่จะเริ่มต้นภายในฟังก์ชั่นrefไม่ได้

กรณีการใช้งานของคำหลักสองคำนี้ก็เกือบจะเหมือนกันและการใช้งานที่บ่อยเกินไปของพวกเขาคือฉันเชื่อว่ามีกลิ่นรหัส (แม้ว่าจะมีกรณีการใช้ที่ถูกต้องเช่นTryParseและTryGetValueรูปแบบ)

ด้วยเหตุนี้ใครบางคนสามารถอธิบายได้ว่าทำไมมีเครื่องมือสองอย่างที่คล้ายกันมากใน C # สำหรับกรณีการใช้งานที่แคบมาก?

นอกจากนี้ในMSDNมีการระบุว่าพวกเขามีพฤติกรรมการใช้งานที่แตกต่างกัน:

แม้ว่าคีย์เวิร์ด ref และ out จะทำให้เกิดพฤติกรรมรันไทม์ที่แตกต่างกัน แต่ก็ไม่ได้พิจารณาว่าเป็นส่วนหนึ่งของลายเซ็นเมธอดในเวลารวบรวม

พฤติกรรมรันไทม์ของพวกเขาแตกต่างกันอย่างไร

ข้อสรุป

คำตอบทั้งสองนั้นดูถูกต้องขอบคุณทั้งคู่ ฉันยอมรับjmorenoเพราะชัดเจนกว่า


ฉันเดาว่าพวกเขาทั้งสองมีการใช้งานเช่นเดียวกับrefใน C ++; เพื่อชี้ไปยังตัวชี้วัตถุ (หรือดั้งเดิม) ในคำถาม ie Int32.TryParse(myStr, out myInt)(C #) คือ "ดำเนินการ" เช่นเดียวกับint32_tryParse(myStr, &myInt)(C) ข้อแตกต่างเพียงข้อ จำกัด บางประการที่คอมไพเลอร์บังคับใช้เพื่อป้องกันข้อบกพร่อง (ฉันจะไม่โพสต์สิ่งนี้เป็นคำตอบเพราะฉันอาจผิดเกี่ยวกับวิธีการทำงานเบื้องหลัง แต่นี่เป็นวิธีที่ฉันจินตนาการว่ามันใช้งานได้ (เพราะสมเหตุสมผล)]
Cole Johnson

คำตอบ:


10

ในช่วงเวลาที่ถามคำถามนี้บทความ MSDN นั้นไม่ถูกต้องอย่างละเอียด ( นับตั้งแต่ได้รับการแก้ไขแล้ว ) แทนที่จะ "ทำให้เกิดพฤติกรรมที่แตกต่าง" ควรเป็น "ต้องการพฤติกรรมที่แตกต่าง"

โดยเฉพาะอย่างยิ่งคอมไพเลอร์บังคับใช้ข้อกำหนดที่แตกต่างกันสำหรับคำหลักสองคำแม้ว่าจะใช้ mecanism (IL) เดียวกันเพื่อเปิดใช้งานลักษณะการทำงาน


ดังนั้นความเข้าใจของฉันคือคอมไพเลอร์ตรวจสอบสิ่งต่าง ๆ เช่น dereferencing outพารามิเตอร์หรือไม่กำหนดrefพารามิเตอร์ แต่ IL มันส่งเสียงเป็นเพียงการอ้างอิงอย่างใดอย่างหนึ่ง ถูกต้องหรือไม่
แอนดรู

Btw, หนึ่งอาจบอกได้จากความจริงที่ว่าout Tและref Tมีการแสดงผลเหมือนกันในภาษา CLI อื่น ๆ เช่น VB.Net หรือ C ++ / CLI
ACH

@AndrewPiliser: ถูกต้อง สมมติว่ารหัสนั้นคอมไพล์ด้วยวิธีใดวิธีหนึ่ง (คุณไม่พยายามที่จะอ้างถึงเมื่อคุณไม่ควร) IL จะเหมือนกัน
jmoreno

4

นี่คือการเขียนที่น่าสนใจในหัวข้อที่อาจตอบคำถามของคุณ:

http://www.dotnetperls.com/ref

จุดสนใจ:

"ความแตกต่างระหว่างการอ้างอิงและการออกไม่ได้อยู่ใน Common Language Runtime แต่อยู่ในภาษา C #"

อัปเดต:

นักพัฒนาอาวุโสในที่ทำงานของฉันเพิ่งยืนยันคำตอบของ @jmoreno ดังนั้นโปรดอ่าน!


ดังนั้นถ้าฉันเข้าใจอย่างถูกต้องไม่มีความแตกต่างในระดับ IL ซึ่งหมายความว่าพวกเขาไม่สามารถมีพฤติกรรมเวลาทำงานที่แตกต่างกัน สิ่งนี้จะขัดแย้งกับสิ่งที่ MSDN พูด บทความที่น่าสนใจยังคง!
Gábor Angyal

@GáborAngyalคุณไม่มีลิงค์ไปยังบทความ MSDN โดยบังเอิญใช่ไหม? มันอาจจะดีถ้ามีความคิดเห็นที่ต่างออกไป
Dan Beaulieu

ที่นี่คุณจะไป: msdn.microsoft.com/en-gb/library/t3c3bfhx.aspxแต่มันเป็นคำถามของฉันตลอด :)
Gábor Angyal

1
สิ่งที่ไม่stringต้องทำอะไรกับทั้งหมดนี้หรือไม่?
Robert Harvey

มันเป็นเพียงการถอดความจากบทความ อัปเดตเพื่อลบบรรทัดที่สอง ..
Dan Beaulieu

2

รันไทม์ ? ไม่มีเลย คุณไม่สามารถโหลดเมธอดที่มีพารามิเตอร์เดียวกันแตกต่างกันโดยการอ้างอิงคำหลักหรือออก

ลองรวบรวมสิ่งนี้และคุณจะได้รับข้อผิดพลาดในการคอมไพล์ "วิธีการที่มีลายเซ็นเดียวกันประกาศแล้ว":

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

เพื่อตอบคำถามนี้: "... ทำไมมีเครื่องมือที่คล้ายกันสองอย่างใน C # สำหรับกรณีการใช้งานที่แคบมาก"

จากความสามารถในการอ่านโค้ดและจุดยืนของ API มีความแตกต่างอย่างมาก ในฐานะผู้บริโภคของ API ฉันรู้ว่าเมื่อใช้ "out" API จะไม่ขึ้นอยู่กับพารามิเตอร์ out ในฐานะนักพัฒนา API ฉันชอบ "ออก" และใช้ "อ้างอิง" เฉพาะเมื่อจำเป็นจริงๆ (RARELY!) ดูการอ้างอิงนี้สำหรับการสนทนาที่ดี:

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

ข้อมูลสนับสนุน: ฉันรวบรวมวิธีการต่อไปนี้และถอดประกอบ ฉันใช้คำหลักอ้างอิงและออก (ในตัวอย่างนี้) แต่รหัสชุดประกอบไม่เปลี่ยนแปลงยกเว้นการอ้างอิงที่อยู่ตามที่คาดไว้:

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

00000000 ผลักดัน ebp
00000001 MOV ebp ทาย
00000003 ผลักดัน EDI
00000004 ผลักดัน ESI
00000005 ผลักดัน ebx
00000006 ย่อย ESP, 34H
00,000,009 xor EAX, EAX
0000000b MOV DWORD PTR [ebp-10h], EAX
0000000e MOV DWORD PTR [ebp-1Ch], EAX
00000011 mov dword ptr [ebp-3Ch], ecx
00000014 mov dword ptr [ebp-40h], edx
00000017 cmp dword ptr ds: [008B1710h], 0
0000001e
00000025
00000020 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 ป๊อป ebx
00000032 ป๊อป esi
00000033 ป๊อปอี
ดิ 00000034 ป๊อป ebp
00000035 ret

ฉันอ่านชุดประกอบได้ถูกต้องหรือไม่?


สิ่งที่คุณเขียนนั้นถูกต้อง แต่โปรดทราบว่าไม่ใช่นัยที่ชัดเจนว่าไม่มีความแตกต่างแบบรันไทม์เนื่องจากไม่อนุญาตให้โอเวอร์โหลดนี้
Gábor Angyal

อ่า - จุดดี! ทุกคนสามารถแยกรหัสข้างต้นและพิสูจน์ฉันถูกหรือผิด? ฉันอ่านหนังสือได้แย่มาก แต่อยากรู้
Shmoken

โอเค - ฉันถอดรหัสโดยใช้ "ออก" และ "อ้างอิง" และฉันไม่เห็นความแตกต่างสำหรับวิธีการ DoSomething ของฉัน:
Shmoken
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.