ฉันต้องการที่จะเข้าใจสถานการณ์ที่IEqualityComparer<T>และIEquatable<T>ควรจะใช้ เอกสาร MSDN ของทั้งคู่มีลักษณะคล้ายกันมาก
T IEquatable<T>คอลเลคชั่นList<T>จะมีบั๊กที่ซ่อนอยู่ในนั้นบ้างไหม?
ฉันต้องการที่จะเข้าใจสถานการณ์ที่IEqualityComparer<T>และIEquatable<T>ควรจะใช้ เอกสาร MSDN ของทั้งคู่มีลักษณะคล้ายกันมาก
T IEquatable<T>คอลเลคชั่นList<T>จะมีบั๊กที่ซ่อนอยู่ในนั้นบ้างไหม?
คำตอบ:
IEqualityComparer<T>Tเป็นอินเตอร์เฟซสำหรับวัตถุที่ดำเนินการเปรียบเทียบวัตถุสองชนิดที่
IEquatable<T>สำหรับวัตถุประเภทTเพื่อให้สามารถเปรียบเทียบตัวเองกับประเภทอื่นที่เหมือนกัน
IEqualityComparer<T>เป็นอินเตอร์เฟซสำหรับวัตถุ(ซึ่งมักจะเป็นระดับที่แตกต่างกันที่มีน้ำหนักเบาจากT)ที่ให้ฟังก์ชั่นการเปรียบเทียบที่ทำงานบนT
เมื่อตัดสินใจว่าจะใช้IEquatable<T>หรือIEqualityComparer<T>มีใครถาม:
มีวิธีที่ต้องการในการทดสอบสองอินสแตนซ์ของ
Tเพื่อความเท่าเทียมกันหรือมีหลายวิธีที่ถูกต้องเท่าเทียมกัน?
หากมีเพียงวิธีเดียวในการทดสอบสองอินสแตนซ์ของTความเท่าเทียมกันหรือหากหนึ่งในหลายวิธีที่ต้องการนั้นIEquatable<T>จะเป็นตัวเลือกที่ถูกต้อง: อินเทอร์เฟซนี้ควรจะดำเนินการด้วยTตัวเองเท่านั้นดังนั้นอินสแตนซ์เดียวTของ Tวิธีการเปรียบเทียบตัวเองไปอินสแตนซ์ของผู้อื่น
ในทางกลับกันหากมีวิธีการที่เหมาะสมหลายวิธีในการเปรียบเทียบสองTs เพื่อความเท่าเทียมกันนั้นIEqualityComparer<T>จะดูเหมาะสมกว่า: อินเตอร์เฟสนี้ไม่ได้หมายถึงการใช้งานด้วยTตัวเอง แต่โดยคลาสอื่น ๆ "ภายนอก" ดังนั้นเมื่อทดสอบสองอินสแตนซ์ของTเพื่อความเท่าเทียมกันเนื่องจากTไม่มีความเข้าใจภายในเกี่ยวกับความเสมอภาคคุณจะต้องเลือกอย่างชัดเจนของIEqualityComparer<T>อินสแตนซ์ที่ดำเนินการทดสอบตามข้อกำหนดเฉพาะของคุณ
ตัวอย่าง:
ลองพิจารณาสองประเภทนี้ (ซึ่งควรจะมีความหมายตามตัวอักษร ):
interface IIntPoint : IEquatable<IIntPoint>
{
int X { get; }
int Y { get; }
}
interface IDoublePoint // does not inherit IEquatable<IDoublePoint>; see below.
{
double X { get; }
double Y { get; }
}
ทำไมประเภทเหล่านี้เพียงประเภทเดียวจึงสืบทอดIEquatable<>แต่ไม่ใช่ประเภทอื่น
ในทางทฤษฎีมีเพียงวิธีเดียวที่สมเหตุสมผลในการเปรียบเทียบสองอินสแตนซ์ของทั้งสองประเภท: พวกเขาจะเท่ากันถ้าXและYคุณสมบัติในทั้งสองกรณีเท่ากัน ตามความคิดนี้ทั้งสองประเภทควรนำมาใช้IEquatable<>เพราะดูเหมือนว่ามีวิธีอื่นที่มีความหมายในการทำแบบทดสอบความเท่าเทียมกัน
ปัญหาที่นี่คือการเปรียบเทียบจำนวนจุดลอยตัวเพื่อความเท่าเทียมกันอาจจะไม่ทำงานตามที่คาดไว้เนื่องจากข้อผิดพลาดนาทีปัดเศษ มีวิธีการที่แตกต่างกันในการเปรียบเทียบตัวเลขทศนิยมสำหรับความเท่าเทียมกันโดยแต่ละวิธีมีข้อดีและข้อเสียต่างกันและคุณอาจต้องการเลือกวิธีที่เหมาะสม
sealed class DoublePointNearEqualityComparerByTolerance : IEqualityComparer<IDoublePoint>
{
public DoublePointNearEqualityComparerByTolerance(double tolerance) { … }
…
public bool Equals(IDoublePoint a, IDoublePoint b)
{
return Math.Abs(a.X - b.X) <= tolerance && Math.Abs(a.Y - b.Y) <= tolerance;
}
…
}
โปรดทราบว่าหน้าเว็บที่ฉันเชื่อมโยงกับ (ด้านบน) ระบุอย่างชัดเจนว่าการทดสอบความเท่าเทียมกันนี้มีจุดอ่อนบางอย่าง เนื่องจากนี่เป็นการIEqualityComparer<T>ใช้งานคุณสามารถสลับมันได้ถ้ามันไม่ดีพอสำหรับวัตถุประสงค์ของคุณ
IEqualityComparer<T>การดำเนินการที่ถูกต้องใด ๆจะต้องใช้ความสัมพันธ์ที่เท่าเทียมกันซึ่งหมายความว่าในทุกกรณีที่วัตถุสองรายการเปรียบเทียบเท่ากับหนึ่งในสามพวกเขาจะต้องเปรียบเทียบกัน คลาสใดที่นำไปใช้IEquatable<T>หรือIEqualityComparer<T>เป็นแฟชั่นที่ตรงกันข้ามกับด้านบนจะใช้งานไม่ได้
IEqualityComparer<String>ที่พิจารณาว่า "สวัสดี" เท่ากับ "Hello" และ "hElLo" จะต้องพิจารณาว่า "Hello" และ "hElLo" เท่ากัน แต่สำหรับวิธีการเปรียบเทียบส่วนใหญ่ที่ไม่มีปัญหา
GetHashCodeควรอนุญาตให้คุณกำหนดได้อย่างรวดเร็วว่าค่าสองค่าแตกต่างกันหรือไม่ กฎมีคร่าว ๆ ดังนี้: (1) GetHashCodeต้องสร้างรหัสแฮชเดียวกันสำหรับค่าเดียวกันเสมอ (2) GetHashCodeควรเร็ว (เร็วกว่าEquals) (3) GetHashCodeไม่จำเป็นต้องแม่นยำ (ไม่แม่นยำเท่าEquals) ซึ่งหมายความว่ามันอาจสร้างรหัสแฮชเดียวกันสำหรับค่าที่แตกต่างกัน ยิ่งคุณแม่นยำมากขึ้นเท่าไหร่ก็ยิ่งดี แต่ก็สำคัญที่จะต้องทำให้มันเร็วขึ้น
คุณมีคำจำกัดความพื้นฐานของสิ่งที่พวกเขาเป็นอยู่แล้ว ในระยะสั้นหากคุณใช้IEquatable<T>ในระดับTที่Equalsวิธีการเกี่ยวกับวัตถุของการพิมพ์ที่Tบอกคุณว่าวัตถุเอง (อย่างใดอย่างหนึ่งที่มีการทดสอบเพื่อความเท่าเทียมกัน) Tจะมีค่าเท่ากับตัวอย่างของประเภทเดียวกันอีก ในขณะที่IEqualityComparer<T>เป็นสำหรับการทดสอบความเท่าเทียมกันของสองกรณีของโดยทั่วไปนอกขอบเขตของอินสแตนซ์ของTT
สำหรับสิ่งที่พวกเขากำลังจะสับสนในตอนแรก จากคำนิยามมันควรจะชัดเจนว่าด้วยเหตุนี้IEquatable<T>(กำหนดไว้ในคลาสTเอง) ควรเป็นมาตรฐาน de พฤตินัยเพื่อแสดงถึงเอกลักษณ์ของวัตถุ / อินสแตนซ์ HashSet<T>, Dictionary<T, U>(พิจารณาGetHashCodeจะถูกแทนเช่นกัน) ContainsในList<T>ฯลฯ ทำให้การใช้นี้ การดำเนินการIEqualityComparer<T>เกี่ยวกับการTไม่ได้ช่วยดังกล่าวข้างต้นกรณีทั่วไป ต่อจากนั้นมีค่าน้อยสำหรับการใช้งานIEquatable<T>ในชั้นอื่น ๆ นอกเหนือจากTนี้ นี้:
class MyClass : IEquatable<T>
ไม่ค่อยมีเหตุผล
ในทางกลับกัน
class T : IEquatable<T>
{
//override ==, !=, GetHashCode and non generic Equals as well
public bool Equals(T other)
{
//....
}
}
มันควรจะทำอย่างไร
IEqualityComparer<T>อาจมีประโยชน์เมื่อคุณต้องการการตรวจสอบความเท่าเทียมกันแบบกำหนดเอง แต่ไม่ใช่กฎทั่วไป ตัวอย่างเช่นในชั้นเรียนPersonณ จุดหนึ่งคุณอาจต้องทดสอบความเท่าเทียมกันของคนสองคนตามอายุของพวกเขา ในกรณีนี้คุณสามารถทำได้:
class Person
{
public int Age;
}
class AgeEqualityTester : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
return x.Age == y.Age;
}
public int GetHashCode(Person obj)
{
return obj.Age.GetHashCode;
}
}
เพื่อทดสอบพวกเขาลอง
var people = new Person[] { new Person { age = 23 } };
Person p = new Person() { age = 23 };
print people.Contains(p); //false;
print people.Contains(p, new AgeEqualityTester()); //true
ในทำนองเดียวกันIEqualityComparer<T>ที่Tไม่สมเหตุสมผล
class Person : IEqualityComparer<Person>
ใช้งานได้จริง แต่ดูไม่ดีต่อสายตาและเอาชนะตรรกะ
IEquatable<T>โดยปกติแล้วสิ่งที่คุณต้องการ นอกจากนี้ในอุดมคติคุณสามารถมีเพียงหนึ่งIEquatable<T>ในขณะที่หลาย ๆIEqualityComparer<T>เป็นไปได้ตามเกณฑ์ที่แตกต่าง
IEqualityComparer<T>และIEquatable<T>เป็นสิ่งที่คล้ายคลึงกับComparer<T>และIComparable<T>ซึ่งจะใช้เพื่อการเปรียบเทียบมากกว่าเท่า; หัวข้อที่ดีที่นี่ที่ฉันเขียนคำตอบเดียวกัน :)
public int GetHashCode(Person obj)ควรกลับมาobj.GetHashCode()
obj.Age.GetHashCodeควรกลับ จะแก้ไข
person.GetHashCodeได้ทุกที่ .... เหตุผลที่คุณจะแทนที่ที่? - เราแทนที่เพราะประเด็นทั้งหมดIEqualityComparerคือการใช้การเปรียบเทียบที่แตกต่างกันตามกฎของเรา - กฎที่เรารู้จักดี
Age Age.GetHashCodeอายุเป็นประเภทintแต่สิ่งที่ประเภทเป็นสัญญารหัสกัญชาใน NET different objects can have equal hash but equal objects cant ever have different hashesเป็นว่า นี่คือสัญญาที่เราเชื่อได้อย่างสุ่มสี่สุ่มห้า แน่นอนว่าผู้เปรียบเทียบที่กำหนดเองของเราควรปฏิบัติตามกฎนี้เช่นกัน หากการเรียกใช้การsomeObject.GetHashCodeแบ่งสิ่งต่าง ๆ แสดงว่าเป็นปัญหาของผู้ใช้งานประเภทsomeObjectไม่ใช่ปัญหาของเรา
IEqualityComparer สำหรับใช้เมื่อความเท่าเทียมกันของวัตถุสองชิ้นถูกนำไปใช้ภายนอกเช่นหากคุณต้องการกำหนดตัวเปรียบเทียบสำหรับสองประเภทที่คุณไม่ได้มีแหล่งที่มาหรือในกรณีที่ความเท่าเทียมกันระหว่างสองสิ่งนั้นสมเหตุสมผลในบริบทที่ จำกัด
IEquatable สำหรับวัตถุนั้นเอง (สิ่งหนึ่งที่ถูกนำมาเปรียบเทียบเพื่อความเท่าเทียมกัน) เพื่อนำไปใช้
หนึ่งเปรียบเทียบสองTs อื่น ๆ ที่สามารถเปรียบเทียบตัวเองกับคนอื่น ๆTs โดยปกติคุณจะต้องใช้งานครั้งละหนึ่งรายการเท่านั้นไม่ใช่ทั้งสองอย่าง
EqualityComparer<T>แทนการใช้อินเตอร์เฟส "เพราะEqualityComparer<T>การทดสอบความเท่าเทียมกันโดยใช้IEquatable<T>