เหตุใด C # และ Java จึงใช้การอ้างอิงที่เท่าเทียมกันเป็นค่าเริ่มต้นสำหรับ '=='


32

ฉันได้รับการขบคิดในขณะที่ว่าทำไม Java และ C # (และฉันแน่ใจว่าภาษาอื่น ๆ ) ==เริ่มต้นความเสมอภาคอ้างอิงสำหรับ

ในการเขียนโปรแกรมที่ฉันทำ (ซึ่งแน่นอนเป็นเพียงส่วนเล็ก ๆ ของปัญหาการเขียนโปรแกรม) ฉันมักจะต้องการความเสมอภาคเชิงตรรกะเมื่อเปรียบเทียบวัตถุแทนการอ้างอิงความเท่าเทียมกัน ฉันพยายามคิดว่าเพราะเหตุใดทั้งสองภาษานี้จึงใช้เส้นทางนี้แทนการย้อนกลับและมี==ความเท่าเทียมทางตรรกะและใช้.ReferenceEquals()เพื่อความเท่าเทียมกันในการอ้างอิง

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

ฉันไม่ต้องการที่จะรู้ปัญหาของการพยายามเปรียบเทียบเชิงตรรกะและมันจะต้องถูกนำไปใช้ในทุกชั้น ฉันยังตระหนักว่าภาษาเหล่านี้ได้รับการออกแบบมานานแล้ว แต่คำถามทั่วไปยืนอยู่

มีประโยชน์ที่สำคัญของการเริ่มต้นสิ่งนี้ที่ฉันเพิ่งหายไปหรือไม่หรือดูเหมือนว่ามีเหตุผลว่าพฤติกรรมเริ่มต้นควรเป็นความเท่าเทียมกันทางตรรกะและการเริ่มต้นกลับไปอ้างอิงความเท่าเทียมกันมันไม่มีความเสมอภาคเชิงตรรกะสำหรับชั้นเรียน?


3
เพราะตัวแปรเป็นการอ้างอิง? เนื่องจากตัวแปรทำตัวเหมือนพอยน์เตอร์มันจึงสมเหตุสมผลสำหรับพวกเขาที่จะถูกนำมาเปรียบเทียบกัน
Daniel Gratzer

C # ใช้ความเท่าเทียมกันเชิงตรรกะสำหรับประเภทค่าเช่น structs แต่สิ่งที่ "ความเท่าเทียมกันทางตรรกะเริ่มต้น" ควรเป็นวัตถุสองชนิดที่มีประเภทการอ้างอิงแตกต่างกันอย่างไร หรือสำหรับวัตถุสองชนิดที่วัตถุประเภทหนึ่งสืบทอดมาจาก B "false" เสมอชอบสำหรับ structs หรือไม่ แม้ว่าคุณจะมีการอ้างอิงวัตถุเดียวกันสองครั้งแรกเป็น A แล้วก็เป็น B ไม่สมเหตุสมผลสำหรับฉัน
Doc Brown

3
คุณกำลังถามว่าทำไมใน C # ถ้าคุณแทนที่Equals()มันจะไม่เปลี่ยนพฤติกรรมโดยอัตโนมัติ==หรือไม่
svick

คำตอบ:


29

C # ทำเช่นนี้เพราะ Java ทำ Java ทำเพราะ Java ไม่รองรับการบรรทุกเกินพิกัด เนื่องจากความเท่าเทียมกันของค่าจะต้องกำหนดใหม่สำหรับแต่ละชั้นจึงไม่สามารถเป็นผู้ดำเนินการได้ แต่ต้องเป็นวิธีการแทน IMO นี่เป็นการตัดสินใจที่ไม่ดี มันง่ายกว่าทั้งในการเขียนและอ่านa == bมากกว่าa.equals(b)และเป็นธรรมชาติมากขึ้นสำหรับโปรแกรมเมอร์ที่มีประสบการณ์ C หรือ C ++ แต่a == bมักจะผิดเสมอ ข้อบกพร่องจากการใช้==ตำแหน่งที่.equalsต้องการทำให้เสียเวลานับพันชั่วโมงโปรแกรมเมอร์


7
ฉันคิดว่ามีผู้สนับสนุนจำนวนมากของผู้ปฏิบัติงานมากเกินไปเนื่องจากมีผู้ว่าดังนั้นฉันจะไม่พูดว่า "มันเป็นการตัดสินใจที่ไม่ดี" เป็นคำสั่งที่สมบูรณ์ ตัวอย่าง: ในโครงการ C ++ ฉันทำงานในเรามากเกินไป==สำหรับหลายชั้นเรียนและไม่กี่เดือนที่ผ่านมาฉันค้นพบนักพัฒนาบางคนไม่ทราบว่า==กำลังทำอะไรอยู่ ยังมีความเสี่ยงนี้อยู่เสมอเมื่อความหมายของโครงสร้างบางส่วนไม่ชัดเจน equals()สัญกรณ์บอกฉันว่าฉันกำลังใช้วิธีการที่กำหนดเองและที่ฉันต้องมองมันขึ้นอยู่ที่ไหนสักแห่ง บรรทัดล่าง: ฉันคิดว่าผู้ประกอบการมากไปเป็นเรื่องเปิดโดยทั่วไป
Giorgio

9
ฉันว่า Java ไม่มีผู้ให้บริการโหลดเกินพิกัด โอเปอเรเตอร์จำนวนมากมีความหมายสองเท่า (โอเวอร์โหลด) ใน Java ดู+ตัวอย่างซึ่งทำการเพิ่ม (ของค่าตัวเลข) และการต่อสตริงในเวลาเดียวกัน
Joachim Sauer

14
จะa == bเป็นธรรมชาติมากขึ้นสำหรับโปรแกรมเมอร์ที่มีประสบการณ์ C เนื่องจาก C ไม่รองรับการใช้งานเกินพิกัดที่ผู้ใช้กำหนด? (ตัวอย่างเช่นวิธี C เพื่อเปรียบเทียบสตริงคือstrcmp(a, b) == 0ไม่a == b.)
svick

นี่เป็นสิ่งที่ฉันคิด แต่ฉันคิดว่าฉันจะขอให้ผู้ที่มีประสบการณ์มากขึ้นเพื่อให้แน่ใจว่าฉันไม่ได้พลาดอะไรที่ชัดเจน
Zip

4
@svick: ใน C ไม่มีประเภทสตริงหรือประเภทอ้างอิงใด ๆ การดำเนินงาน String char *จะทำผ่านทาง เห็นได้ชัดสำหรับฉันว่าการเปรียบเทียบสองพอยน์เตอร์เพื่อความเท่าเทียมไม่เหมือนกับการเปรียบเทียบสตริง
วินไคลน์

15

คำตอบสั้น ๆ : ความสอดคล้อง

เพื่อที่จะตอบคำถามของคุณอย่างถูกต้องฉันขอแนะนำให้คุณย้อนกลับไปดูปัญหาของความเท่าเทียมกันในภาษาการเขียนโปรแกรม มีความเป็นไปได้ที่แตกต่างกันอย่างน้อยสามอย่างซึ่งใช้ในภาษาต่างๆ:

  • Reference equality : หมายถึง a = b เป็นจริงถ้า a และ b อ้างถึงวัตถุเดียวกัน มันจะไม่เป็นจริงถ้า a และ b อ้างถึงวัตถุต่าง ๆ แม้ว่าคุณลักษณะทั้งหมดของ a และ b จะเหมือนกัน
  • ตื้นเสมอภาค : หมายความว่า a = b เป็นจริงถ้าคุณลักษณะทั้งหมดของวัตถุที่ a และ b อ้างถึงเหมือนกัน ความเท่าเทียมกันของตื้นสามารถนำมาใช้ได้อย่างง่ายดายโดยการเปรียบเทียบระดับบิตของพื้นที่หน่วยความจำที่แสดงถึงวัตถุทั้งสอง โปรดทราบว่าความเท่าเทียมกันอ้างอิงหมายถึงความเท่าเทียมกันตื้น
  • Deep equality : หมายถึง a = b เป็นจริงถ้าแต่ละแอตทริบิวต์ใน a และ b นั้นเหมือนกันหรือลึกเท่ากัน โปรดทราบว่าความเท่าเทียมกันอย่างลึกซึ้งนั้นมีความหมายโดยทั้งความเท่าเทียมกันในการอ้างอิงและความเท่าเทียมที่ตื้น ในแง่นี้ความเท่าเทียมกันลึก ๆ คือรูปแบบที่อ่อนแอที่สุดของความเท่าเทียมกันและความเท่าเทียมกันในการอ้างอิงคือสิ่งที่แข็งแกร่งที่สุด

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

ในระบบที่ไม่สำคัญความเท่าเทียมกันของวัตถุมักจะถูกกำหนดเป็นบางสิ่งบางอย่างระหว่างความลึกและความเท่าเทียมกันอ้างอิง ในการตรวจสอบว่าเราต้องการคำนึงถึงวัตถุสองชิ้นเท่ากันในบริบทหนึ่งหรือไม่เราอาจต้องการคุณลักษณะบางอย่างที่จะเปรียบเทียบโดยที่มันอยู่ในหน่วยความจำและอื่น ๆ ด้วยความเท่าเทียมกันลึกในขณะที่คุณลักษณะบางอย่างอาจได้รับอนุญาตให้แตกต่างกัน สิ่งที่เราต้องการคือ“ ความเสมอภาคแบบสี่” ซึ่งเป็นสิ่งที่ดีจริงๆซึ่งมักถูกเรียกว่าในความหมายเชิงวรรณกรรม ทุกสิ่งเท่าเทียมกันในโดเมนของเรา =)

ดังนั้นเราสามารถกลับมาที่คำถามของคุณ:

มีประโยชน์ที่สำคัญของการเริ่มต้นสิ่งนี้ที่ฉันเพิ่งหายไปหรือไม่หรือดูเหมือนว่าเหตุผลที่พฤติกรรมเริ่มต้นควรเป็นความเท่าเทียมกันทางตรรกะและการเริ่มต้นกลับไปอ้างอิงความเท่าเทียมกันถ้าไม่มีความเท่าเทียมกันเชิงตรรกะสำหรับชั้นเรียน?

เราหมายถึงอะไรเมื่อเราเขียน 'a == b' ในภาษาใด ๆ โดยหลักการแล้วมันควรจะเหมือนกัน: ความเสมอภาคทางความหมาย แต่นั่นเป็นไปไม่ได้

หนึ่งในข้อควรพิจารณาหลักคืออย่างน้อยสำหรับประเภทอย่างง่ายเช่นตัวเลขเราคาดว่าตัวแปรสองตัวจะเท่ากันหลังจากการกำหนดค่าเดียวกัน ดูด้านล่าง:

var a = 1;
var b = a;
if (a == b){
    ...
}
a = 3;
b = 3;
if (a == b) {
    ...
}

ในกรณีนี้เราคาดหวังว่า 'a เท่ากับ b' ในทั้งสองข้อความ สิ่งอื่นใดที่จะเสียสติ ภาษาส่วนใหญ่ (ถ้าไม่ใช่ทั้งหมด) เป็นไปตามอนุสัญญานี้ ดังนั้นด้วยประเภทที่เรียบง่าย (ค่า aka) เรารู้วิธีการบรรลุความเท่าเทียมกันทางความหมาย ด้วยวัตถุนั่นอาจเป็นสิ่งที่แตกต่างอย่างสิ้นเชิง ดูด้านล่าง:

var a = new Something(1);
var b = a;
if (a == b){
    ...
}
b = new Something(1);
a.DoSomething();
b.DoSomething();
if (a == b) {
    ...
}

เราคาดหวังว่า 'ถ้า' คนแรกจะเป็นจริงเสมอ แต่สิ่งที่คุณคาดหวังในที่สอง 'ถ้า'? มันขึ้นอยู่กับ 'DoSomething' สามารถเปลี่ยนความเท่าเทียมกัน (semantic) ของ a และ b ได้หรือไม่?

ปัญหาเกี่ยวกับความหมายของความเท่าเทียมกันก็คือว่ามันไม่สามารถสร้างขึ้นโดยอัตโนมัติโดยรวบรวมวัตถุหรือ it's เห็นได้ชัดจากงานที่มอบหมาย ต้องจัดเตรียมกลไกสำหรับผู้ใช้เพื่อกำหนด semantic equality ในภาษาเชิงวัตถุกลไกที่เป็นวิธีการที่ได้รับมรดกมา: เท่ากับ การอ่านรหัส OO บางส่วนเราไม่คาดหวังว่าวิธีการจะมีการใช้งานที่แน่นอนเหมือนกันในทุกชั้นเรียน เราคุ้นเคยกับการสืบทอดและการบรรทุกเกินพิกัด

อย่างไรก็ตามด้วยผู้ประกอบการเราคาดหวังว่าพฤติกรรมเดียวกัน เมื่อคุณเห็น 'a == b' คุณควรคาดหวังความเท่าเทียมแบบเดียวกัน (จาก 4 ด้านบน) ในทุกสถานการณ์ ดังนั้นการมีจุดมุ่งหมายเพื่อความมั่นคงนักออกแบบภาษาจึงใช้ความเท่าเทียมกันในการอ้างอิงสำหรับทุกประเภท ไม่ควรขึ้นอยู่กับว่าโปรแกรมเมอร์เขียนทับวิธีการหรือไม่

ป.ล. : ภาษาดีแตกต่างจาก Java และ C # เล็กน้อย: ตัวดำเนินการเท่ากับหมายถึงความเสมอภาคแบบตื้นสำหรับประเภทที่เรียบง่ายและความเท่าเทียมกันทางความหมายสำหรับคลาสที่ผู้ใช้กำหนดเอง ในฐานะที่เป็นประเภทที่เรียบง่ายความเสมอภาคตื้นตื้นมักจะมีความหมายเชิง semantic เสมอกันภาษานั้นสอดคล้องกัน แม้ว่าราคาที่จ่ายนั้นจะเท่ากับว่าผู้ให้บริการเท่ากับโดยค่าเริ่มต้นไม่ได้กำหนดสำหรับประเภทที่ผู้ใช้กำหนด คุณต้องใช้มัน และบางครั้งก็น่าเบื่อ


2
When you see ‘a == b’ you should expect the same type of equality (from the 4 above) in all situations.ผู้ออกแบบภาษาของ Java ใช้ความเท่าเทียมกันอ้างอิงสำหรับวัตถุและความเท่าเทียมกันทางความหมายสำหรับดั้งเดิม ไม่ชัดเจนสำหรับฉันว่านี่เป็นการตัดสินใจที่ถูกต้องหรือการตัดสินใจครั้งนี้ "มีความสอดคล้อง" มากกว่าการอนุญาตให้==ใช้งานมากเกินไปสำหรับความเท่าเทียมกันทางความหมายของวัตถุ
Charles Salvia

พวกเขาใช้ "เทียบเท่าของความเท่าเทียมกันอ้างอิง" สำหรับดึกดำบรรพ์เช่นกัน เมื่อคุณใช้ "int i = 3" ไม่มีตัวชี้สำหรับหมายเลขดังนั้นคุณจึงไม่สามารถใช้การอ้างอิงได้ ด้วยสตริงซึ่งเป็นประเภทดั้งเดิม "เรียงลำดับ" มันชัดเจนมากขึ้น: คุณต้องใช้ ".intern ()" หรือการกำหนดโดยตรง (String s = "abc") เพื่อใช้ == (ความเท่าเทียมกันอ้างอิง)
Hbas

1
PS: C # ในทางกลับกันไม่สอดคล้องกับสตริง และในกรณีนี้ IMHO นั้นดีกว่ามาก
Hbas

@CharlesSalvia: ใน Java หากaและbเป็นประเภทเดียวกันนิพจน์a==bจะทดสอบว่ามีaและbถือสิ่งเดียวกันหรือไม่ หากหนึ่งในนั้นมีการอ้างอิงไปยังวัตถุ # 291 และอื่น ๆ ถือการอ้างอิงไปยังวัตถุ # 572 พวกเขาไม่ได้ถือสิ่งเดียวกัน เนื้อหาของวัตถุ # 291 และ # 572 อาจจะเทียบเท่า แต่ตัวแปรที่ตัวเองถือสิ่งที่แตกต่าง
supercat

2
@CharlesSalvia มันถูกออกแบบมาในแบบที่คุณสามารถดูa == bและรู้ว่ามันทำอะไร ในทำนองเดียวกันคุณสามารถดูa.equals(b)และเข้าใจว่า equalsoverloads หากมีการa == bโทรa.equals(b)(ถ้ามีการใช้งาน) จะเป็นการเปรียบเทียบโดยการอ้างอิงหรือเนื้อหา จำไม่ได้เหรอ? คุณต้องตรวจสอบคลาส A รหัสนั้นไม่สามารถอ่านได้อย่างรวดเร็วอีกต่อไปหากคุณไม่แน่ใจว่ามีการเรียกใช้อะไร มันจะเหมือนกับว่าวิธีการที่มีลายเซ็นเดียวกันได้รับอนุญาตและวิธีการที่ถูกเรียกขึ้นอยู่กับสิ่งที่ขอบเขตปัจจุบัน โปรแกรมดังกล่าวจะเป็นไปไม่ได้ที่จะอ่าน
Neil

0

ฉันพยายามคิดว่าทำไมทั้งสองภาษาเหล่านี้จึงใช้เส้นทางนี้แทนการย้อนกลับและมี == เป็นความเสมอภาคเชิงตรรกะและการใช้. ReferenceEquals () เพื่อความเท่าเทียมกันในการอ้างอิง

เพราะวิธีการหลังจะสับสน พิจารณา:

if (null.ReferenceEquals(null)) System.out.println("ok");

ควรพิมพ์รหัสนี้"ok"หรือควรโยนNullPointerException?


-2

สำหรับ Java และ C # ประโยชน์อยู่ที่การวางเชิงวัตถุ

จากมุมมองประสิทธิภาพ - รหัสที่ง่ายต่อการเขียนควรเร็วกว่า: เนื่องจาก OOP ตั้งใจที่จะให้องค์ประกอบที่แตกต่างเชิงตรรกะแสดงโดยวัตถุที่แตกต่างกันการตรวจสอบความเท่าเทียมกันของการอ้างอิงจะเร็วขึ้นโดยคำนึงถึงว่า

จากมุมมองเชิงตรรกะ - ความเท่าเทียมกันของวัตถุหนึ่งไปอีกอันหนึ่งไม่จำเป็นต้องมีความชัดเจนเท่ากับการเปรียบเทียบกับคุณสมบัติของวัตถุเพื่อความเท่าเทียมกัน (เช่นวิธี null == null ตีความตรรกะอย่างมีเหตุมีผล?

ฉันคิดว่าสิ่งที่ทำให้คุณเดือดร้อนคือการสังเกตของคุณว่า "คุณต้องการความเสมอภาคทางตรรกะมากกว่าความเสมอภาคอ้างอิง" ฉันทามติในหมู่นักออกแบบภาษาอาจตรงกันข้าม ฉันพบว่าเป็นการยากที่จะประเมินสิ่งนี้เป็นการส่วนตัวเนื่องจากฉันขาดประสบการณ์การเขียนโปรแกรมในวงกว้าง โดยประมาณฉันใช้ความเท่าเทียมในการอ้างอิงมากขึ้นในอัลกอริธึมการปรับให้เหมาะสมที่สุดและความเท่าเทียมทางตรรกะในการจัดการชุดข้อมูล


7
ความเท่าเทียมกันของการอ้างอิงไม่มีส่วนเกี่ยวข้องกับการวางแนววัตถุ ค่อนข้างตรงข้ามจริง ๆ : หนึ่งในคุณสมบัติพื้นฐานของการวางแนววัตถุคือวัตถุที่มีพฤติกรรมเดียวกันจะแยกไม่ออก วัตถุหนึ่งจะต้องสามารถจำลองวัตถุอื่นได้ (หลังจากทั้งหมด OO ถูกประดิษฐ์ขึ้นสำหรับการจำลอง!) ความเท่าเทียมกันของการอ้างอิงช่วยให้คุณสามารถแยกแยะความแตกต่างระหว่างวัตถุสองชนิดที่มีพฤติกรรมเหมือนกันมันช่วยให้คุณสามารถแยกแยะความแตกต่างระหว่างวัตถุจำลองกับวัตถุจริง ดังนั้นความเท่าเทียมกันอ้างอิงแบ่งการวางแนววัตถุ โปรแกรม OO จะต้องไม่ใช้ Reference Equality
Jörg W Mittag

@ JörgWMittag: การทำโปรแกรมเชิงวัตถุอย่างถูกต้องนั้นจำเป็นต้องมีวิธีการถาม object X ว่าสถานะของมันเท่ากับของ Y หรือไม่ [เงื่อนไขชั่วคราวที่อาจเกิดขึ้น] และวิธีการถามวัตถุ X ว่าเทียบเท่ากับ Y หรือไม่ [X เทียบเท่ากับ Y เฉพาะเมื่อสถานะของมันรับประกันว่าจะเท่ากับนิรันดร์ Y] การมีวิธีเสมือนแยกต่างหากสำหรับความเท่าเทียมและความเท่าเทียมกันของรัฐจะดี แต่สำหรับหลาย ๆ ประเภทความไม่เท่าเทียมกันของการอ้างอิงจะบ่งบอกถึงความไม่เท่าเทียมกันและไม่มีเหตุผลที่จะใช้เวลาในการจัดส่งวิธีเสมือนเพื่อพิสูจน์มัน
supercat

-3

.equals()เปรียบเทียบตัวแปรตามเนื้อหา แทนที่จะ==เปรียบเทียบวัตถุตามเนื้อหา ...

การใช้วัตถุนั้นมีความแม่นยำมากขึ้น .equals()


3
การสันนิษฐานของคุณไม่ถูกต้อง . Equals () ทำทุกสิ่ง. Equals () ทำรหัสให้ทำ โดยปกติแล้วจะเป็นเนื้อหา แต่ไม่จำเป็นต้องเป็น นอกจากนี้ยังไม่แม่นยำในการใช้. Equals () มันขึ้นอยู่กับสิ่งที่คุณพยายามทำให้สำเร็จ
Zip
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.