เนื่องจากคอลเลกชันเช่นSystem.Collections.Generic.HashSet<>
ยอมรับnull
เป็นสมาชิกชุดหนึ่งสามารถถามว่ารหัสแฮชnull
ควรเป็นอย่างไร ดูเหมือนว่ากรอบจะใช้0
:
// nullable struct type
int? i = null;
i.GetHashCode(); // gives 0
EqualityComparer<int?>.Default.GetHashCode(i); // gives 0
// class type
CultureInfo c = null;
EqualityComparer<CultureInfo>.Default.GetHashCode(c); // gives 0
สิ่งนี้อาจเป็นปัญหา (เล็กน้อย) กับ enums ที่เป็นโมฆะ ถ้าเรากำหนด
enum Season
{
Spring,
Summer,
Autumn,
Winter,
}
จากนั้นNullable<Season>
(เรียกอีกอย่างว่าSeason?
) สามารถรับค่าได้เพียงห้าค่า แต่สองค่าคือnull
และSeason.Spring
มีรหัสแฮชเหมือนกัน
เป็นเรื่องน่าดึงดูดที่จะเขียนตัวเปรียบเทียบความเท่าเทียมที่ "ดีกว่า" ดังนี้:
class NewNullEnumEqComp<T> : EqualityComparer<T?> where T : struct
{
public override bool Equals(T? x, T? y)
{
return Default.Equals(x, y);
}
public override int GetHashCode(T? x)
{
return x.HasValue ? Default.GetHashCode(x) : -1;
}
}
แต่มีเหตุผลใดบ้างที่null
ควรเป็นรหัสแฮช0
?
แก้ไข / เพิ่มเติม:
Object.GetHashCode()
บางคนดูเหมือนจะคิดว่านี่เป็นเรื่องเกี่ยวกับการเอาชนะ มันไม่ได้เป็นจริง (ผู้เขียนของ .NET ก็ทำให้การแทนที่ของGetHashCode()
ในNullable<>
struct ซึ่งเป็นที่เกี่ยวข้องแม้ว่า.) การดำเนินการใช้งานเป็นลายลักษณ์อักษรจาก parameterless ไม่สามารถจัดการกับสถานการณ์ที่วัตถุที่มีรหัสกัญชาเราแสวงหาก็คือGetHashCode()
null
นี้เป็นเรื่องเกี่ยวกับการดำเนินการตามวิธีนามธรรมหรือมิฉะนั้นการใช้วิธีการอินเตอร์เฟซEqualityComparer<T>.GetHashCode(T)
IEqualityComparer<T>.GetHashCode(T)
ตอนนี้ขณะที่การสร้างการเชื่อมโยงเหล่านี้เพื่อ MSDN ผมเห็นว่ามันบอกว่ามีที่วิธีการเหล่านี้โยนArgumentNullException
ถ้าอาร์กิวเมนต์ null
แต่เพียงผู้เดียวของพวกเขาคือ นี่ต้องเป็นความผิดพลาดของ MSDN อย่างแน่นอน? การใช้งานของ. NET ไม่มีข้อยกเว้น การขว้างปาในกรณีนั้นจะทำลายความพยายามใด ๆ ที่จะเพิ่มลงnull
ในไฟล์HashSet<>
. เว้นแต่HashSet<>
จะมีบางสิ่งที่พิเศษเมื่อจัดการกับnull
ไอเท็ม (ฉันจะต้องทดสอบสิ่งนั้น)
แก้ไขใหม่ / เพิ่มเติม:
ตอนนี้ฉันพยายามดีบัก ด้วยHashSet<>
ฉันสามารถยืนยันได้ว่าด้วยตัวเปรียบเทียบความเท่าเทียมเริ่มต้นค่าSeason.Spring
และnull
จะสิ้นสุดในที่เก็บข้อมูลเดียวกัน นี้จะถูกกำหนดโดยอย่างระมัดระวังตรวจสอบสมาชิกของอาร์เรย์ส่วนตัวและm_buckets
m_slots
โปรดทราบว่าดัชนีมักจะหักล้างกันโดยการออกแบบ
อย่างไรก็ตามรหัสที่ฉันให้ไว้ข้างต้นไม่สามารถแก้ไขปัญหานี้ได้ ปรากฎว่าHashSet<>
จะไม่ถามตัวเปรียบเทียบความเท่าเทียมกันด้วยซ้ำว่าค่าเป็นnull
อย่างไร นี่มาจากซอร์สโค้ดของHashSet<>
:
// Workaround Comparers that throw ArgumentNullException for GetHashCode(null).
private int InternalGetHashCode(T item) {
if (item == null) {
return 0;
}
return m_comparer.GetHashCode(item) & Lower31BitMask;
}
ซึ่งหมายความว่าอย่างน้อยHashSet<>
ก็เป็นไปไม่ได้ที่จะเปลี่ยนแฮชของnull
. วิธีแก้ปัญหาคือเปลี่ยนแฮชของค่าอื่น ๆ ทั้งหมดดังนี้:
class NewerNullEnumEqComp<T> : EqualityComparer<T?> where T : struct
{
public override bool Equals(T? x, T? y)
{
return Default.Equals(x, y);
}
public override int GetHashCode(T? x)
{
return x.HasValue ? 1 + Default.GetHashCode(x) : /* not seen by HashSet: */ 0;
}
}