ReferenceEquals คืนค่าเท็จสำหรับตัวแปรที่อ้างถึงอินสแตนซ์โครงสร้างเดียวกัน

นี่คือโค้ดของฉันซึ่งฉันพยายามเรียกใช้เมธอด object.ReferenceEquals กับตัวแปรสองตัวที่อ้างอิงถึงอินสแตนซ์โครงสร้างเดียวกัน:

static void Main(string[] args)
{
    var myref = new Group();
    var myref2 = myref;
    if (object.ReferenceEquals(myref, myref2))
    {
        Console.WriteLine("The references are equal.");
    }
    else
    {
        Console.WriteLine("The references are NOT equal.");
    }
}

โครงสร้าง Group ได้รับการกำหนดไว้ด้านล่าง:

struct Group
{
    public int StudentCount { get; set; }
}

สิ่งที่น่าสนใจคือมันจะพิมพ์ข้อความในบล็อก else ซึ่งแนะนำว่า myref และ myref2 กำลังชี้ไปที่วัตถุที่แตกต่างกัน:

ข้อมูลอ้างอิงไม่เท่ากัน

ทำไมสิ่งนี้ถึงเป็นไปได้? ฉันสร้างอินสแตนซ์ของ Group struct เพียงครั้งเดียวเมื่อเริ่มต้นฟังก์ชัน Main


person RBT    schedule 16.06.2018    source แหล่งที่มา
comment
โครงสร้างเป็นประเภทค่า คุณอาจลองสร้างการอ้างอิงสองรายการไปยัง int เดียวกัน คุณไม่สามารถ; คุณจะสร้างสำเนาสองชุดแทน   -  person John Wu    schedule 16.06.2018
comment
โปรดทราบว่า object.ReferenceEquals รับสองวัตถุ โครงสร้างจะถูกบรรจุอยู่ในวัตถุสองชิ้นที่แตกต่างกันบนฮีป ดังนั้นจึงไม่มีการอ้างอิงเท่ากัน   -  person Mike Zboray    schedule 16.06.2018
comment
@mikez ฮ่าๆ นี่เป็นเรื่องสมเหตุสมผล จริงๆ แล้วมันเป็นวิธีการเรียก object.ReferenceEquals ซึ่งก่อให้เกิดความมหัศจรรย์ทั้งหมด ดังนั้นจนถึงจุดที่วิธีการ ReferenceEquals ถูกเรียกในคำสั่ง if นั้น myref และ myref2 ก็ชี้ไปที่ตำแหน่งหน่วยความจำเดียวกัน ถูกต้อง?   -  person RBT    schedule 16.06.2018
comment
@RBT ไม่ ไม่ใช่ตำแหน่งหน่วยความจำเดียวกัน แต่เป็นตัวแปรที่แตกต่างกัน สิ่งที่ฉันได้รับคือแม้ว่าคุณจะเปรียบเทียบตัวแปร struct เดียวกันกับตัวมันเอง แต่ก็ไม่ได้อ้างอิงเท่ากันเนื่องจากการชกมวย   -  person Mike Zboray    schedule 16.06.2018


คำตอบ (2)


โครงสร้างถูกส่งผ่านโดย value ไม่ใช่โดยการอ้างอิง คุณมีโครงสร้างสองอินสแตนซ์ เนื่องจากในการกำหนด ค่าจะถูกคัดลอก - ไม่มีการอ้างอิง

person cwharris    schedule 16.06.2018
comment
ดังนั้น เพียงแค่มอบหมายงาน var myref2 = myref; ฉันกำลังสร้างอินสแตนซ์ใหม่ 2 รายการที่มี Group ในหน่วยความจำ ใช่ไหม? - person RBT; 16.06.2018
comment
นั่นคือวิธีการทำงานของโครงสร้างใช่ พวกมันจะถูกจัดสรรบนสแต็ก เช่นเดียวกับจำนวนเต็ม ทศนิยม สองเท่า ฯลฯ และดังนั้นจึงถูกจัดสรรคืนเมื่อขอบเขตออก เทียบกับอินสแตนซ์ของคลาสที่ถูกจัดสรรบนฮีปและใคร อ้างอิง ถูกจัดสรรคืนเมื่อออก ขอบเขต แต่ค่าของใครจะต้องเป็นขยะที่รวบรวมจากฮีปเมื่อไม่มีการอ้างอิงอีกต่อไป - person cwharris; 16.06.2018
comment
นั่นคือความแตกต่างหลักระหว่างคลาสและโครงสร้าง - การจัดสรรฮีปเทียบกับสแต็กตามลำดับ - person cwharris; 16.06.2018
comment
แก้ไข... คุณไม่ได้จัดสรร สองอินสแตนซ์ใหม่ คุณกำลังจัดสรร หนึ่ง อินสแตนซ์ใหม่ myref ถูกคัดลอกไปที่ myref2 แล้ว แต่ myref ยังคงใช้หน่วยความจำเท่าเดิม และในทางเทคนิคแล้ว สิ่งเหล่านี้ไม่ใช่อินสแตนซ์... เป็นเพียงหน่วยความจำที่จัดสรรบนสแต็ก - person cwharris; 16.06.2018
comment
ฉันจะแตกต่างออกไปเล็กน้อยใน - That is the primary difference between classes and structs - heap vs stack allocation, respectively. : นี่ ไม่ จริงทั้งหมดตามที่กล่าวไว้ ที่นี่ - person RBT; 16.06.2018
comment
ยุติธรรมเพียงพอ ฉันกำลังพูดจากมุมมองของการนำไปปฏิบัติจริงๆ - person cwharris; 16.06.2018

ฉันค้นคว้าเพิ่มเติมและในที่สุดก็พบสิ่งที่เป็นรูปธรรมตามความคิดเห็นของ mike z การสร้างส่วนที่เกี่ยวข้องของ นี้ บล็อกที่พูดถึงพฤติกรรมที่สำคัญมากของ ReferenceEquals ในกรณีของ structs ซึ่งเป็นประเภทค่า ที่นี่แทนที่โครงสร้าง เขาได้นำค่าจำนวนเต็มซึ่งเป็นประเภทค่าด้วย ผลลัพธ์ยังคงเหมือนเดิม

TL;DR; Object.ReferenceEquals(valueVar, valueVar) จะ เสมอ ส่งคืนค่า false สำหรับ struct ไม่ว่าจะเป็นตัวแปรเดียวกันหรือตัวแปรที่แตกต่างกัน

รายละเอียด: โดยปกติแล้วเมธอด ReferenceEquals ใช้เพื่อพิจารณาว่าวัตถุสองรายการเป็นอินสแตนซ์เดียวกันหรือไม่ แต่คุณต้องระมัดระวังเล็กน้อยเมื่อใช้กับประเภทค่า พิจารณารหัสต่อไปนี้:

static void Main(string[] args)
{
    int valueVar = 15;

    if (Object.ReferenceEquals(valueVar, valueVar))
        Console.WriteLine("Reference Equal");
    else
        Console.WriteLine("Reference Not Equal");

    Console.ReadLine();
} // Will always print "Reference Not Equal"

รหัสนี้จะพิมพ์ "การอ้างอิงไม่เท่ากัน" เสมอตราบใดที่ valueVar เป็นตัวแปรประเภทค่า (ซึ่งรวมถึง struct ด้วย) หากคุณตรวจสอบ Object.ReferenceEquals มันถูกออกแบบมาเพื่อรับสองวัตถุเป็นพารามิเตอร์อินพุต ดังนั้นเมื่อคุณส่ง Value Types ไปให้ .NET จะดำเนินการต่อไปและ "Boxes" พารามิเตอร์ นี่คือ MSIL ที่เกี่ยวข้องซึ่งสร้างขึ้นสำหรับโค้ดด้านบน

ป้อนคำอธิบายรูปภาพที่นี่

"Boxing" นี้ส่งผลให้มีการสร้างออบเจ็กต์ที่แตกต่างกันสองรายการบนฮีปซึ่งตอนนี้จะใช้เพื่อทำการเปรียบเทียบ คุณสามารถใช้ SOS เพื่อตรวจสอบสิ่งนี้

ป้อนคำอธิบายรูปภาพที่นี่

ดังนั้นเราจึงลงเอยด้วยการทำ ReferenceEquals ระหว่างวัตถุที่ 0x01d917e0 (ที่อยู่ของวัตถุหนึ่ง) และ 0x01d917ec (ที่อยู่ของวัตถุอื่น) ซึ่งเห็นได้ชัดว่าจะล้มเหลว (โปรดจำไว้ว่าตอนนี้เป็นวัตถุประเภทอ้างอิง ดังนั้นที่อยู่จะถูกเปรียบเทียบ) และส่งคืนค่าเท็จ

เพื่อสรุปแม้ว่า Object.ReferenceEquals(valueVar, valueVar) จะส่งผ่านตัวแปรเดียวกันกับพารามิเตอร์ทั้งสอง แต่จะส่งกลับค่า false เสมอ

person RBT    schedule 16.06.2018
comment
ขออภัยในความไร้เดียงสาของฉันที่นี่ แต่ดูเหมือนว่าคำตอบด้านล่างนี้จะง่ายกว่าและตรงไปตรงมามากกว่า โครงสร้างเป็นประเภทค่าและประเภทค่าไม่ได้อ้างอิงถึงสิ่งใดเลย - โครงสร้างเป็นเพียงแค่เก็บค่าไว้ในหน่วยความจำเท่านั้น ตามที่ฉันเข้าใจ การใช้ ReferenceEquals กับประเภทค่าจะคืนค่าเท็จเสมอ ไม่แน่ใจว่าทำไมจึงไม่เกิดข้อยกเว้น แต่บางทีนั่นอาจเป็นเพียงการตัดสินใจแบบเดิมในระดับ CLR - person Christian Findlay; 23.05.2020