GetHashCode แนวทางใน C #


136

ฉันอ่านในหนังสือ Essential C # 3.0 และ. NET 3.5 ที่:

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

นี่เป็นแนวทางที่ถูกต้องหรือไม่?

ฉันได้ลองใช้บิวด์อินสองตัวใน. NET และพวกเขาก็ไม่ทำตัวแบบนี้


คุณอาจต้องการพิจารณาเปลี่ยนคำตอบที่ยอมรับถ้าเป็นไปได้
Giffyguy

คำตอบ:


93

คำตอบนั้นส่วนใหญ่เป็นแนวทางที่ถูกต้อง แต่อาจไม่ใช่กฎที่ถูกต้อง มันไม่ได้บอกเรื่องราวทั้งหมด

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

ตัวอย่างเช่นวัตถุ A คืนค่าแฮชของ 1 ดังนั้นมันจะอยู่ใน bin 1 ของตารางแฮช จากนั้นคุณเปลี่ยนวัตถุ A เพื่อให้ส่งคืนค่าแฮชของ 2 เมื่อตารางแฮชมองหามันจะค้นหาใน bin 2 และไม่พบมัน - วัตถุนั้นถูกกำพร้าในถัง 1 นี่คือสาเหตุที่รหัสแฮชต้อง ไม่เปลี่ยนแปลงสำหรับอายุการใช้งานของวัตถุและเพียงเหตุผลหนึ่งว่าทำไมการเขียนการใช้งานของ GetHashCode จึงเป็นความเจ็บปวดในก้น

ปรับปรุง
Eric Lippert ได้โพสต์บล็อกGetHashCodeที่ให้ข้อมูลที่ดีใน

อัปเดตเพิ่มเติม
ฉันได้ทำการเปลี่ยนแปลงสองสามข้อข้างต้น:

  1. ฉันแยกความแตกต่างระหว่างแนวทางและกฎ
  2. ฉันหลงทาง "ตลอดชีวิตของวัตถุ"

แนวทางเป็นเพียงแนวทางไม่ใช่กฎ ในความเป็นจริงGetHashCodeมีเพียงทำตามแนวทางเหล่านี้เมื่อสิ่งที่คาดหวังว่าวัตถุที่จะปฏิบัติตามแนวทางเช่นเมื่อมันถูกเก็บไว้ในตารางแฮช หากคุณไม่เคยต้องการใช้วัตถุของคุณในตารางแฮช (หรือสิ่งอื่นใดที่อาศัยกฎของGetHashCode) การใช้งานของคุณไม่จำเป็นต้องทำตามคำแนะนำ

เมื่อคุณเห็น "ตลอดอายุการใช้งานของวัตถุ" คุณควรอ่าน "ในเวลาที่วัตถุต้องร่วมมือกับตารางแฮช" หรือคล้ายกัน เช่นเดียวกับสิ่งส่วนใหญ่GetHashCodeเกี่ยวกับการรู้ว่าเมื่อใดจะทำลายกฎ


1
คุณจะกำหนดความเท่าเทียมกันระหว่างประเภทที่ไม่แน่นอนได้อย่างไร
Jon B

9
คุณไม่ควรใช้ GetHashCode เพื่อกำหนดความเท่าเทียมกัน
JSB ձոգչ

4
@JS Bangs - จาก MSDN: คลาสที่ได้รับซึ่งแทนที่ GetHashCode จะต้องแทนที่ Equals เพื่อรับประกันว่าวัตถุสองชิ้นที่ถือว่าเท่ากันจะมีรหัสแฮชเดียวกัน มิฉะนั้นประเภท Hashtable อาจทำงานไม่ถูกต้อง
Jon B

3
@Joan Venge: สองสิ่ง อย่างแรกไม่ใช่แม้แต่ Microsoft ที่มี GetHashCode อยู่ในทุกการใช้งาน ประการที่สองประเภทค่ามักจะไม่เปลี่ยนรูปทุกค่าเป็นอินสแตนซ์ใหม่มากกว่าการแก้ไขอินสแตนซ์ที่มีอยู่
Jeff Yates

17
เนื่องจาก a.Equals (b) ต้องหมายความว่า a.GetHashCode () == b.GetHashCode () รหัสแฮชส่วนใหญ่จะต้องเปลี่ยนหากข้อมูลที่ใช้สำหรับการเปรียบเทียบความเท่าเทียมกันมีการเปลี่ยนแปลง ฉันจะบอกว่าปัญหาไม่ได้ GetHashCode เป็นไปตามข้อมูลที่ไม่แน่นอน ปัญหาคือการใช้วัตถุที่ไม่แน่นอนเป็นคีย์ตารางแฮช (และการกลายพันธุ์จริง ๆ ) ฉันผิดหรือเปล่า?
Niklas

120

เป็นเวลานานแล้ว แต่อย่างไรก็ตามฉันคิดว่ามันยังคงจำเป็นที่จะต้องให้คำตอบที่ถูกต้องสำหรับคำถามนี้รวมถึงคำอธิบายเกี่ยวกับ whys และ hows คำตอบที่ดีที่สุดคือการอ้างถึง MSDN อย่างละเอียด - อย่าพยายามสร้างกฎของคุณเองพวก MS รู้ว่าพวกเขากำลังทำอะไรอยู่

แต่สิ่งแรกสิ่งแรก: แนวทางที่อ้างถึงในคำถามนั้นผิด

ตอนนี้พวกเขามีสองคน

เหตุผลแรก : หากคำนวณ hashcode ด้วยวิธีการนั้นจะไม่เปลี่ยนแปลงในช่วงอายุการใช้งานของวัตถุแม้ว่าตัววัตถุจะเปลี่ยนแปลงไปกว่าที่มันจะทำลายสัญญาเท่ากับ

เตือนความจำ: "ถ้าวัตถุสองรายการเปรียบเทียบเท่ากันเมธอด GetHashCode สำหรับแต่ละวัตถุจะต้องส่งคืนค่าเดียวกันอย่างไรก็ตามหากวัตถุสองรายการไม่เปรียบเทียบกันเท่ากันเมธอด GetHashCode สำหรับวัตถุทั้งสองนั้นไม่จำเป็นต้องส่งคืนค่าที่ต่างกัน"

ประโยคที่สองมักตีความผิด ๆ ว่า "กฎข้อเดียวคือเมื่อเวลาสร้างวัตถุ hashcode ของวัตถุที่เท่ากันจะต้องเท่ากัน" ไม่รู้จริงๆว่าทำไม แต่นั่นเป็นเรื่องเกี่ยวกับสาระสำคัญของคำตอบส่วนใหญ่ที่นี่เช่นกัน

นึกถึงวัตถุสองชิ้นที่มีชื่อโดยใช้ชื่อในวิธีการเท่ากับ: ชื่อเดียวกัน -> สิ่งเดียวกัน สร้างอินสแตนซ์ A: ชื่อ = โจสร้างอินสแตนซ์ B: ชื่อ = ปีเตอร์

Hashcode A และ Hashcode B ส่วนใหญ่จะไม่เหมือนกัน จะเกิดอะไรขึ้นเมื่อชื่อของอินสแตนซ์ B เปลี่ยนเป็นโจ

ตามแนวทางจากคำถามแฮชโค้ดของ B จะไม่เปลี่ยนแปลง ผลลัพธ์ของสิ่งนี้จะเป็น: A.Equals (B) ==> จริง แต่ในเวลาเดียวกัน: A.GetHashCode () == B.GetHashCode () ==> false

แต่พฤติกรรมนี้ถูกห้ามอย่างชัดเจนโดยเท่ากับ & hashcode-contract

เหตุผลที่สอง : ในขณะที่มันเป็นจริงการเปลี่ยนแปลงในแฮชโค้ดอาจทำให้รายการแฮชและวัตถุอื่น ๆ ที่ใช้แฮชโค้ดกลับกันก็เป็นจริงเช่นกัน การไม่เปลี่ยนแฮชโค้ดจะเป็นกรณีที่แย่ที่สุดที่จะได้รับรายการแฮชที่วัตถุต่าง ๆ จำนวนมากจะมีแฮชโค้ดเดียวกันและจะอยู่ในแฮชถังเดียวกัน - เกิดขึ้นเมื่อวัตถุถูกเริ่มต้นด้วยค่ามาตรฐานตัวอย่างเช่น


ทีนี้มาถึงจุดต่ำสุดแล้วในแวบแรกดูเหมือนว่าจะมีความขัดแย้ง - ทั้งสองทางรหัสจะแตก แต่ไม่มีปัญหามาจากการเปลี่ยนแปลงหรือ hashcode ไม่เปลี่ยนแปลง

สาเหตุของปัญหาได้อธิบายไว้อย่างดีใน MSDN:

จากรายการ hashtable ของ MSDN:

วัตถุหลักจะต้องไม่เปลี่ยนรูปตราบเท่าที่ใช้เป็นกุญแจใน Hashtable

นี่หมายความว่า:

วัตถุใด ๆ ที่สร้าง hashvalue ควรเปลี่ยน hashvalue เมื่อวัตถุนั้นเปลี่ยนแปลง แต่จะต้องไม่ - แน่นอนต้องไม่ - อนุญาตการเปลี่ยนแปลงใด ๆ กับตัวมันเองเมื่อมันถูกใช้ภายใน Hashtable (หรือวัตถุอื่นที่ใช้ Hash แน่นอน) .

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

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

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

อย่างไรก็ตามสิ่งนี้จำเป็นต้องมีวิธีการที่เท่าเทียมกันไม่ได้ขึ้นอยู่กับข้อมูลที่ไม่แน่นอนเช่นกัน ดังนั้นถ้าฉันเขียนวัตถุและสร้างเมธอด GetHashCode ที่คำนวณค่าเพียงครั้งเดียวและเก็บไว้ในวัตถุเพื่อส่งคืนในการโทรในภายหลังฉันต้องอีกครั้ง: ต้องสร้างวิธีการเท่ากับที่จะใช้อย่างแน่นอน ค่าที่เก็บไว้สำหรับการเปรียบเทียบเพื่อให้ A.Equals (B) จะไม่เปลี่ยนจาก false เป็น true เช่นกัน มิฉะนั้นสัญญาจะแตก ผลลัพธ์ของการทำเช่นนี้มักจะเป็นที่วิธีการ Equals นั้นไม่มีเหตุผลใด ๆ - มันไม่ใช่การอ้างอิงแบบเดิม แต่มันก็ไม่ได้มีค่าเท่ากับ บางครั้งสิ่งนี้อาจเป็นพฤติกรรมที่ตั้งใจ (เช่นบันทึกลูกค้า) แต่โดยปกติจะไม่

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

(โดยวิธีการ: ทั้งหมดนี้ไม่เฉพาะ C # oder. NET - เป็นลักษณะของการใช้งาน hashtable ทั้งหมดหรือโดยทั่วไปของรายการดัชนีใด ๆ ที่ระบุข้อมูลของวัตถุไม่ควรเปลี่ยนแปลงในขณะที่วัตถุอยู่ในรายการ พฤติกรรมที่ไม่คาดคิดและคาดเดาไม่ได้จะเกิดขึ้นถ้ากฎนี้ใช้งานไม่ได้ที่ไหนสักแห่งอาจมีการใช้รายการซึ่งจะตรวจสอบองค์ประกอบทั้งหมดในรายการและทำการทำดัชนีรายการใหม่โดยอัตโนมัติ - แต่ประสิทธิภาพของสิ่งเหล่านั้นจะน่ากลัวอย่างที่สุด)


23
+1 สำหรับคำอธิบายรายละเอียดนี้ (จะให้มากขึ้นถ้าฉันสามารถ)
โอลิเวอร์

5
+1 นี่เป็นคำตอบที่ดีกว่าเพราะคำอธิบายอย่างละเอียด! :)
Joe

9

จากMSDN

ถ้าวัตถุสองรายการเปรียบเทียบเท่ากันวิธี GetHashCode สำหรับแต่ละวัตถุจะต้องส่งคืนค่าเดียวกัน อย่างไรก็ตามถ้าวัตถุสองรายการเปรียบเทียบไม่เท่ากันเมธอด GetHashCode สำหรับวัตถุทั้งสองนั้นไม่จำเป็นต้องส่งคืนค่าที่แตกต่างกัน

กระบวนการ GetHashCode วิธีการสำหรับวัตถุต้องส่งคืนรหัสแฮชเดียวกันอย่างสม่ำเสมอตราบใดที่ไม่มีการแก้ไขสถานะของวัตถุที่กำหนดค่าส่งคืนของวิธีการเท่ากับของวัตถุ โปรดทราบว่านี่เป็นความจริงสำหรับการเรียกใช้แอปพลิเคชั่นปัจจุบันเท่านั้นและสามารถส่งคืนรหัสแฮชที่แตกต่างกันได้หากแอปพลิเคชันนั้นทำงานอีกครั้ง

เพื่อประสิทธิภาพที่ดีที่สุดฟังก์ชันแฮชต้องสร้างการแจกแจงแบบสุ่มสำหรับอินพุตทั้งหมด

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


แก้ไข :

ยังมาจาก MSDN:

คลาสที่ได้รับซึ่งแทนที่ GetHashCode จะต้องแทนที่เท่ากับเพื่อรับประกันว่าวัตถุสองชิ้นที่ถือว่าเท่ากันจะมีรหัสแฮชเดียวกัน มิฉะนั้นประเภท Hashtable อาจทำงานไม่ถูกต้อง

จากรายการ hashtable ของ MSDN :

วัตถุหลักจะต้องไม่เปลี่ยนรูปตราบเท่าที่ใช้เป็นกุญแจใน Hashtable

วิธีที่ฉันอ่านนี่คือวัตถุที่เปลี่ยนแปลงได้ควรคืนค่าแฮชโค้ดที่แตกต่างกันเมื่อค่าของมันเปลี่ยนแปลงเว้นแต่ว่ามันถูกออกแบบมาเพื่อใช้ใน hashtable

ในตัวอย่างของ System.Drawing.Point วัตถุไม่แน่นอนและไม่กลับมาเป็นแฮชโค้ดแตกต่างกันเมื่อ X หรือ Y การเปลี่ยนแปลงในมูลค่า สิ่งนี้จะทำให้มันเป็นผู้สมัครที่น่าสงสารที่จะใช้ตามที่เป็นอยู่ใน hashtable


GetHashCode () ถูกออกแบบมาเพื่อใช้ใน hashtable นั่นคือจุดเดียวของฟังก์ชั่นนี้
skolima

@skolima - เอกสาร MSDN ไม่สอดคล้องกับสิ่งนั้น วัตถุที่ไม่แน่นอนอาจใช้ GetHashCode () และควรคืนค่าที่แตกต่างกันตามการเปลี่ยนแปลงค่าของวัตถุ Hashtables ต้องใช้คีย์ที่ไม่เปลี่ยนรูป ดังนั้นคุณสามารถใช้ GetHashCode () สำหรับสิ่งอื่นที่ไม่ใช่ hashtable
Jon B

9

ฉันคิดว่าเอกสารเกี่ยวกับ GetHashcode ค่อนข้างสับสน

ในอีกด้านหนึ่ง MSDN ระบุว่า hashcode ของวัตถุไม่ควรเปลี่ยนแปลงและคงที่ในทางกลับกัน MSDN ยังระบุว่าค่าส่งคืนของ GetHashcode ควรเท่ากับ 2 วัตถุหากวัตถุทั้งสองนั้นถือว่าเท่ากัน

MSDN:

ฟังก์ชันแฮชจะต้องมีคุณสมบัติดังต่อไปนี้:

  • ถ้าวัตถุสองรายการเปรียบเทียบเท่ากันวิธี GetHashCode สำหรับแต่ละวัตถุจะต้องส่งคืนค่าเดียวกัน อย่างไรก็ตามถ้าวัตถุสองรายการเปรียบเทียบไม่เท่ากันเมธอด GetHashCode สำหรับวัตถุทั้งสองนั้นไม่จำเป็นต้องส่งคืนค่าที่แตกต่างกัน
  • กระบวนการ GetHashCode วิธีการสำหรับวัตถุต้องส่งคืนรหัสแฮชเดียวกันอย่างสม่ำเสมอตราบใดที่ไม่มีการแก้ไขสถานะของวัตถุที่กำหนดค่าส่งคืนของวิธีการเท่ากับของวัตถุ โปรดทราบว่านี่เป็นความจริงสำหรับการเรียกใช้แอปพลิเคชั่นปัจจุบันเท่านั้นและสามารถส่งคืนรหัสแฮชที่แตกต่างกันได้หากแอปพลิเคชันนั้นทำงานอีกครั้ง
  • เพื่อประสิทธิภาพที่ดีที่สุดฟังก์ชันแฮชต้องสร้างการแจกแจงแบบสุ่มสำหรับอินพุตทั้งหมด

จากนั้นหมายความว่าวัตถุทั้งหมดของคุณควรไม่เปลี่ยนรูปหรือวิธี GetHashcode ควรขึ้นอยู่กับคุณสมบัติของวัตถุของคุณที่ไม่เปลี่ยนรูป สมมติว่าคุณมีคลาสนี้ (ใช้งานไร้เดียงสา):

public class SomeThing
{
      public string Name {get; set;}

      public override GetHashCode()
      {
          return Name.GetHashcode();
      }

      public override Equals(object other)
      {
           SomeThing = other as Something;
           if( other == null ) return false;
           return this.Name == other.Name;
      }
}

การใช้งานนี้ละเมิดกฎที่สามารถพบได้ใน MSDN สมมติว่าคุณมี 2 อินสแตนซ์ของคลาสนี้ คุณสมบัติชื่อของ instance1 ถูกตั้งค่าเป็น 'Pol' และชื่อคุณสมบัติของ instance2 ถูกตั้งค่าเป็น 'Piet' อินสแตนซ์ทั้งสองคืนค่าแฮชโค้ดที่ต่างกันและจะไม่เท่ากัน ตอนนี้สมมติว่าฉันเปลี่ยนชื่ออินสแตนซ์ 2 เป็น 'Pol' จากนั้นตามเมธอด Equals ของฉันทั้งสองอินสแตนซ์ควรเท่ากันและตามกฎข้อใดข้อหนึ่งของ MSDN พวกเขาควรคืนค่าแฮชโค้ดเดียวกัน
อย่างไรก็ตามสิ่งนี้ไม่สามารถทำได้เนื่องจากแฮชโค้ดของ instance2 จะเปลี่ยนไปและ MSDN ระบุว่าไม่อนุญาต

จากนั้นหากคุณมีเอนทิตีคุณอาจใช้แฮชโค้ดเพื่อให้ใช้ 'ตัวระบุหลัก' ของเอนทิตีนั้นซึ่งอาจเป็นคีย์ตัวแทนหรือคุณสมบัติที่ไม่เปลี่ยนรูป หากคุณมีวัตถุค่าคุณสามารถใช้ Hashcode เพื่อใช้ 'คุณสมบัติ' ของวัตถุค่านั้น คุณสมบัติเหล่านั้นประกอบขึ้นเป็น 'นิยาม' ของวัตถุค่า นี่เป็นลักษณะของวัตถุที่มีค่า คุณไม่ได้สนใจในตัวตนของมัน แต่เป็นคุณค่าของมัน
และดังนั้นวัตถุที่มีคุณค่าควรจะไม่เปลี่ยนรูป (เช่นเดียวกับที่อยู่ในกรอบงาน. NET, สตริง, วันที่, ฯลฯ ... เป็นวัตถุที่เปลี่ยนรูปแบบไม่ได้)

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


1
ตัวอย่างของคุณคือสาเหตุที่ Jeff Yates บอกว่าคุณไม่สามารถยึดรหัสแฮชกับข้อมูลที่ไม่แน่นอนได้ คุณไม่สามารถติดวัตถุที่ไม่แน่นอนในพจนานุกรมและคาดหวังว่ามันจะทำงานได้ดีถ้ารหัสแฮชตามค่าที่ไม่แน่นอนของวัตถุนั้น
Ogre Psalm33

3
ฉันไม่สามารถดูว่ากฎ MSDN ละเมิดหรือไม่ กฎชัดเจนว่า: วิธี GetHashCode สำหรับวัตถุอย่างต่อเนื่องจะต้องกลับรหัสกัญชาเดียวกันตราบใดที่มีการปรับเปลี่ยนไปสู่สถานะของวัตถุที่กำหนดค่าตอบแทนของวิธีเท่ากับวัตถุไม่มี ซึ่งหมายความว่าอนุญาตให้แฮชโค้ดของ instance2 ถูกเปลี่ยนเมื่อคุณเปลี่ยนชื่อของ instance2 เป็น Pol
chikak

8

นี่คือคำแนะนำที่ดี นี่คือสิ่งที่ Brian Pepin พูดถึงในเรื่องนี้:

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


ฉันไม่ได้ลงคะแนน แต่ฉันเดาว่าคนอื่นทำเพราะเป็นข้อความที่ไม่ครอบคลุมปัญหาทั้งหมด แกล้งทำเป็นสตริงที่ไม่แน่นอน แต่ไม่ได้เปลี่ยนรหัสแฮช คุณสร้าง "bob" ใช้เป็นคีย์ใน hashtable แล้วเปลี่ยนค่าเป็น "phil" ถัดไปสร้างสตริงใหม่ "phil" หากคุณมองหารายการตารางแฮชที่มีคีย์ "phil" รายการที่คุณใส่ไว้จะไม่ถูกค้นพบ หากมีคนค้นหาคำว่า "bob" จะพบ แต่คุณจะได้รับค่าที่อาจไม่ถูกต้องอีกต่อไป อาจจะขยันที่จะไม่ใช้กุญแจที่เปลี่ยนแปลงไม่ได้หรือระวังอันตราย
Eric Tuttleman

@EricTuttleman: Were ฉันเขียนกฎสำหรับกรอบผมจะได้ระบุว่าสำหรับคู่ของวัตถุใด ๆXและYเมื่อX.Equals(Y)หรือY.Equals(X)ได้รับการเรียกว่าทุกสายในอนาคตควรผลผลิตผลเดียวกัน หากต้องการใช้คำจำกัดความอื่นของความเท่าเทียมให้ใช้EqualityComparer<T>.
supercat

5

ไม่ตอบคำถามของคุณโดยตรง แต่ถ้าคุณใช้ Resharper อย่าลืมว่ามันมีคุณสมบัติที่สร้างการใช้งาน GetHashCode ที่สมเหตุสมผล (เช่นเดียวกับวิธี Equals) สำหรับคุณ แน่นอนคุณสามารถระบุสมาชิกของชั้นเรียนที่จะนำมาพิจารณาเมื่อคำนวณ hashcode


ขอบคุณจริง ๆ แล้วฉันไม่เคยใช้ Resharper มาก่อน แต่ฉันเห็นว่ามันพูดถึงบ่อยครั้งดังนั้นฉันควรลองดู
Joan Venge

+1 Resharper หากมีมันสร้างการใช้งานที่ดี GetHashCode
ΩmegaMan

5

ลองดูโพสต์บล็อกนี้จาก Marc Brooks:

VTOs, RTOs และ GetHashCode () - โอ้ฉัน!

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

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


4

hashcode ไม่มีการเปลี่ยนแปลง แต่สิ่งสำคัญคือต้องเข้าใจว่ารหัส Hashcode มาจากที่ใด

หากวัตถุของคุณใช้ค่าซีแมนทิกส์นั่นคือเอกลักษณ์ของวัตถุนั้นถูกกำหนดโดยค่าของมัน (เช่นสตริง, สี, โครงสร้างทั้งหมด) หากตัวตนของวัตถุของคุณเป็นอิสระจากค่าทั้งหมดแล้ว Hashcode จะถูกระบุโดยชุดย่อยของค่าของมัน ตัวอย่างเช่นรายการ StackOverflow ของคุณถูกเก็บไว้ในฐานข้อมูลบางแห่ง หากคุณเปลี่ยนชื่อหรืออีเมลรายการลูกค้าของคุณจะยังคงเหมือนเดิมแม้ว่าค่าบางอย่างจะเปลี่ยนไป (ในที่สุดคุณมักจะระบุด้วยรหัสลูกค้ายาว ๆ )

ดังนั้นในระยะสั้น:

ซีแมนทิกส์ประเภทค่า - แฮชโค้ดถูกกำหนดโดยค่าซีแมนทิกส์ประเภทการอ้างอิง - แฮชโค้ดจะถูกกำหนดโดย id บางตัว

ฉันขอแนะนำให้คุณอ่านการออกแบบการขับเคลื่อนด้วยโดเมนโดย Eric Evans ซึ่งเขาเข้าสู่ประเภทหน่วยงานเทียบกับประเภทมูลค่า


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

คุณพูดถูกประเภทค่าไม่เปลี่ยนรูปดังนั้นพวกเขาจึงห้ามการเปลี่ยนแปลง จับดี.
DavidN

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