ความแตกต่างระหว่าง IEquatable และเพียงแค่เอาชนะ Object.Equals () คืออะไร?


185

ฉันต้องการของฉันระดับเพื่อให้สามารถทดสอบเมื่อใดก็ตามที่มีค่าเท่ากับอินสแตนซ์ของผู้อื่นFood Foodฉันจะใช้มันในภายหลังกับรายการและฉันต้องการใช้List.Contains()วิธีของมัน ฉันควรจะใช้IEquatable<Food>หรือเพียงแค่แทนที่Object.Equals()? จาก MSDN:

วิธีนี้จะกำหนดความเท่าเทียมกันโดยใช้การเปรียบเทียบความเท่าเทียมกันเริ่มต้นตามที่กำหนดโดยการใช้งานของวัตถุของวิธีการ IEquatable.Equals สำหรับ T (ชนิดของค่าในรายการ)

ดังนั้นคำถามต่อไปของฉันคือ: ฟังก์ชัน / คลาสใดของกรอบงาน. NET ใช้ประโยชน์จากObject.Equals()อะไร? ฉันควรใช้มันตั้งแต่แรกไหม?


3
คำอธิบายที่ดีมากที่นี่blogs.msdn.com/b/jaredpar/archive/2009/01/15/...
Nawfal

ความเป็นไปได้ที่ซ้ำกันของการทำความเข้าใจ IEquatable
nawfal

คำตอบ:


214

เหตุผลหลักคือประสิทธิภาพ เมื่อยาชื่อสามัญที่ถูกนำมาใช้ใน NET 2.0 พวกเขาก็สามารถที่จะเพิ่มพวงของการเรียนเรียบร้อยเช่นList<T>, Dictionary<K,V>, HashSet<T>ฯลฯ โครงสร้างเหล่านี้ทำให้การใช้งานหนักและGetHashCode Equalsแต่สำหรับประเภทค่านี้มวยที่จำเป็น IEquatable<T>ช่วยให้โครงสร้างใช้Equalsวิธีการพิมพ์อย่างยิ่งดังนั้นจึงไม่จำเป็นต้องใช้มวย ประสิทธิภาพที่ดีขึ้นมากเมื่อใช้ประเภทค่ากับการรวบรวมทั่วไป

ประเภทการอ้างอิงไม่ได้รับประโยชน์มากนัก แต่การIEquatable<T>ใช้งานจะช่วยให้คุณหลีกเลี่ยงการส่งข้อความSystem.Objectซึ่งสามารถสร้างความแตกต่างได้หากเรียกว่าบ่อยครั้ง

ตามที่ระบุไว้ในบล็อกของ Jared Parsonคุณยังต้องใช้การแทนที่ออบเจ็กต์


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

7
นั่นเป็นจริงใน C ++ แต่ไม่ใช่ภาษา. NET ที่บังคับใช้ความปลอดภัยของประเภท มีการร่ายรันไทม์และหากการร่ายไม่สำเร็จข้อยกเว้นจะถูกส่งออกไป ดังนั้นจึงมีค่าปรับเล็กน้อยสำหรับการคัดเลือกนักแสดง คอมไพเลอร์สามารถออปติไมซ์ของ upcast ได้เช่นอ็อบเจกต์ o = (object) "string"; แต่ downcasting - สตริง s = (สตริง) o; - ต้องเกิดขึ้นที่รันไทม์
Josh

1
ฉันเห็น. โดยบังเอิญคุณมีสถานที่ที่ฉันสามารถรับข้อมูล "ลึก" ชนิดนั้นเกี่ยวกับ. NET? ขอบคุณ!
elysium กลืนกิน

7
ฉันจะแนะนำ CLR ผ่าน C # โดย Jeff Richter และ C # ในเชิงลึกโดย Jon Skeet สำหรับบล็อก, บล็อก Wintellect นั้นดี, บล็อก msdn, ฯลฯ
Josh

ที่ไม่IEquatable<T>อินเตอร์เฟซที่ต้องทำอะไรมากไปกว่าเตือนนักพัฒนาที่จะรวมถึง public bool Equals(T other) สมาชิกในชั้นเรียนหรือ struct หรือไม่ การมีหรือไม่มีอินเทอร์เฟซทำให้ไม่มีความแตกต่างในเวลาทำงาน เกินจำนวนที่Equalsจะเป็นสิ่งที่จำเป็น
mikemay

48

ตามที่MSDN :

หากคุณใช้งานIEquatable<T>คุณควรแทนที่การใช้งานคลาสพื้นฐาน Object.Equals(Object)และGetHashCode เพื่อให้พฤติกรรมของพวกเขาสอดคล้องกับIEquatable<T>.Equals วิธีการ หากคุณลบล้าง Object.Equals(Object)การดำเนินการแทนที่ของคุณจะถูกเรียกในการเรียกใช้Equals(System.Object, System.Object)วิธีการคงที่ในชั้นเรียนของคุณ สิ่งนี้ทำให้มั่นใจได้ว่าการร้องขอทั้งหมดของEqualsวิธีส่งคืนผลลัพธ์ที่สอดคล้องกัน

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

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


9
Structs ควรใช้ iEquatable (ของพวกเขาเป็นเจ้าของเอง) แน่นอนหากพวกเขาจะใช้เป็นกุญแจในพจนานุกรมหรือคอลเลกชันที่คล้ายกัน มันจะเพิ่มประสิทธิภาพการทำงานที่สำคัญ คลาสที่ไม่สามารถสืบทอดได้จะได้รับการเพิ่มประสิทธิภาพเล็กน้อยโดยการใช้ IEquatable (ของพวกเขาเองประเภท) คลาสที่สืบทอดได้ควร // ไม่ // ใช้ IEquatable
supercat

30

การขยายสิ่งที่ Josh พูดด้วยแบบอย่างที่เป็นประโยชน์ +1 ถึง Josh - ฉันกำลังจะเขียนเหมือนกันในคำตอบของฉัน

public abstract class EntityBase : IEquatable<EntityBase>
{
    public EntityBase() { }

    #region IEquatable<EntityBase> Members

    public bool Equals(EntityBase other)
    {
        //Generic implementation of equality using reflection on derived class instance.
        return true;
    }

    public override bool Equals(object obj)
    {
        return this.Equals(obj as EntityBase);
    }

    #endregion
}

public class Author : EntityBase
{
    public Author() { }
}

public class Book : EntityBase
{
    public Book() { }
}

ด้วยวิธีนี้ฉันมีวิธี Equals () ที่สามารถนำกลับมาใช้ใหม่ได้ทันทีที่ออกจากกล่องสำหรับคลาสที่ได้รับทั้งหมดของฉัน


อีกหนึ่งคำถาม ข้อดีของการใช้ "obj เป็น EntityBase" แทนที่จะเป็น (EntityBase) obj คืออะไร แค่เรื่องของสไตล์หรือมีข้อได้เปรียบอะไรบ้าง?
elysium กลืนกิน

22
ในกรณีของ "obj as EntityBase" - หาก obj ไม่ได้เป็นประเภท EntityBase มันจะผ่าน "null" และดำเนินการต่อโดยไม่มีข้อผิดพลาดหรือข้อยกเว้นใด ๆ แต่ในกรณีของ "(EntityBase) obj" มันจะพยายามบังคับ obj เพื่อ EntityBase และถ้า obj ไม่ได้เป็นประเภท EntityBase มันจะโยน InvalidCastException และใช่ "เป็น" สามารถใช้ได้กับประเภทการอ้างอิงเท่านั้น
สิ่งนี้ __curious_geek

1
ลิงก์ของ Josh ไปยังบล็อกของ Jared Par ดูเหมือนจะแนะนำให้คุณต้องแทนที่ GetHashCode ด้วย นี่ไม่ใช่กรณีหรือไม่
กันเอง

3
ฉันไม่ได้รับคุณค่าเพิ่มเติมจากการติดตั้งของคุณ คุณช่วยอธิบายปัญหาที่คลาสฐานนามธรรมของคุณแก้ไขได้หรือไม่?
Mert Akcakaya

1
@Amicable - ใช่ทุกครั้งที่คุณแทนที่ Object.Equals (Object) คุณต้องแทนที่ GetHashCode เพื่อให้คอนเทนเนอร์ทำงานได้
namford

0

ถ้าเราเรียกobject.Equalsมันจะบังคับให้มวยราคาแพงในประเภทค่า สิ่งนี้ไม่เป็นที่ต้องการในสถานการณ์ที่อ่อนไหวต่อประสิทธิภาพ IEquatable<T>การแก้ปัญหาคือการใช้งาน

public interface IEquatable<T>
{
  bool Equals (T other);
}

แนวคิดเบื้องหลังIEquatable<T>คือให้ผลเหมือนกันobject.Equalsแต่เร็วกว่า ข้อ จำกัดwhere T : IEquatable<T>จะต้องใช้กับประเภททั่วไปเช่นด้านล่าง

public class Test<T> where T : IEquatable<T>
{
  public bool IsEqual (T a, T b)
  {
    return a.Equals (b); // No boxing with generic T
  }
}

slower object.Equals()มิฉะนั้นจะผูกกับ

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