2009-02-19 30 views
25

Nói cho một lớp Point2, và sau Equals:Cách triển khai tốt nhất Equals cho các loại tùy chỉnh?

public override bool Equals (object obj) 

public bool Equals (Point2 obj) 

Đây là những gì thể hiện trong Effective C# 3:

public override bool Equals (object obj) 
{ 
    // STEP 1: Check for null 
    if (obj == null) 
    { 
     return false; 
    } 

    // STEP 3: equivalent data types 
    if (this.GetType () != obj.GetType ()) 
    { 
     return false; 
    } 
    return Equals ((Point2) obj); 
} 

public bool Equals (Point2 obj) 
{ 
    // STEP 1: Check for null if nullable (e.g., a reference type) 
    if (obj == null) 
    { 
     return false; 
    } 
    // STEP 2: Check for ReferenceEquals if this is a reference type 
    if (ReferenceEquals (this, obj)) 
    { 
     return true; 
    } 
    // STEP 4: Possibly check for equivalent hash codes 
    if (this.GetHashCode () != obj.GetHashCode ()) 
    { 
     return false; 
    } 
    // STEP 5: Check base.Equals if base overrides Equals() 
    System.Diagnostics.Debug.Assert (
     base.GetType () != typeof (object)); 

    if (!base.Equals (obj)) 
    { 
     return false; 
    } 

    // STEP 6: Compare identifying fields for equality. 
    return ((this.X.Equals (obj.X)) && (this.Y.Equals (obj.Y))); 
} 

Trả lời

18

Có một tập hợp toàn bộ guidelines on MSDN là tốt. Bạn nên đọc chúng tốt, nó vừa khó và vừa quan trọng.

Một vài điểm tôi thấy hữu ích nhất:

  • loại giá trị gia tăng không có danh tính, vì vậy trong một struct Point bạn thường sẽ làm một thành viên của thành viên so sánh.

  • Các loại tham chiếu thường có danh tính và do đó kiểm tra bình đẳng thường dừng ở ReferenceEquals (mặc định, không cần ghi đè). Nhưng có những ngoại lệ, như chuỗi và class Point2 của bạn, trong đó đối tượng không có nhận dạng hữu ích và sau đó bạn ghi đè lên các thành viên Bình đẳng để cung cấp ngữ nghĩa của riêng bạn. Trong tình huống đó, hãy làm theo các hướng dẫn để vượt qua các trường hợp null và các kiểu khác trước.

  • Và có nhiều lý do hợp lý để giữ cho GethashCode()operator== cũng đồng bộ hóa.

28

Trong một mà phải mất một obj, nếu loại obj là Point2, gọi loại Equals cụ thể. Bên trong loại Equals cụ thể, hãy chắc chắn rằng tất cả các thành viên đều có cùng giá trị.

public override bool Equals (object obj) 
{ 
    return Equals(obj as Point2); 
} 

public bool Equals (Point2 obj) 
{ 
    return obj != null && obj.X == this.X && obj.Y == this.Y ... 
    // Or whatever you think qualifies as the objects being equal. 
} 

Có thể bạn cũng nên ghi đè GetHashCode để đảm bảo rằng các đối tượng "bằng nhau" có cùng mã băm.

+0

Wont này thổi lên nếu 'đối tượng obj' là một cấu trúc? – row1

+0

@ row1 No. Cấu trúc sẽ được đóng hộp và cấu trúc đóng hộp sẽ dẫn đến 'null' khi toán tử' as' được đánh giá. Nếu kiểu được định nghĩa là một cấu trúc thì bạn cần phải thực hiện một diễn viên rõ ràng thay vì sử dụng 'as' (hoặc sử dụng phiên bản có thể vô hiệu của cấu trúc). Bạn cũng sẽ loại bỏ các kiểm tra null nếu nó là một cấu trúc không nullable. – Servy

+0

Woow rất đơn giản và consise Thực hiện, tôi thích nó, thanx. –

2
  • Xác định định danh có nghĩa là gì .. nếu danh tính tham chiếu thì giá trị mặc định bằng nhau sẽ hoạt động.
  • Nếu loại giá trị (và do đó định danh giá trị) bạn cần xác định.
  • Nếu loại lớp, nhưng có ngữ nghĩa giá trị thì hãy xác định.

Có khả năng bạn muốn ghi đè cả hai đối tượng (đối tượng) và xác định Bằng (đối tượng) vì sau này không tránh boxing. Và ghi đè toán tử bình đẳng.

Sách hướng dẫn Khuôn khổ .NET (phiên bản thứ 2) có mức độ phù hợp hơn.

0

Lie Daniel L cho biết,

public override bool Equals(object obj) { 
    Point2 point = obj as Point2; // Point2? if Point2 is a struct 
    return point != null && this.Equals(point); 
} 

public bool Equals(Point2 point) { 
    ... 
} 
-1
public override bool Equals (object obj) 
{ 
    // struct 
    return obj is Point2 && Equals ( (Point2) value); 
    // class 
    //return Equals (obj as Point2); 
} 

public bool Equals (Point2 obj) 
7

Kỹ thuật tôi đã sử dụng đã làm việc cho tôi như sau. Lưu ý, tôi chỉ so sánh dựa trên một thuộc tính duy nhất (Id) thay vì hai giá trị. Điều chỉnh khi cần

using System; 
namespace MyNameSpace 
{ 
    public class DomainEntity 
    { 
     public virtual int Id { get; set; } 

     public override bool Equals(object other) 
     { 
      return Equals(other as DomainEntity); 
     } 

     public virtual bool Equals(DomainEntity other) 
     { 
      if (other == null) { return false; } 
      if (object.ReferenceEquals(this, other)) { return true; } 
      return this.Id == other.Id; 
     } 

     public override int GetHashCode() 
     { 
      return this.Id; 
     } 

     public static bool operator ==(DomainEntity item1, DomainEntity item2) 
     { 
      if (object.ReferenceEquals(item1, item2)) { return true; } 
      if ((object)item1 == null || (object)item2 == null) { return false; } 
      return item1.Id == item2.Id; 
     } 

     public static bool operator !=(DomainEntity item1, DomainEntity item2) 
     { 
      return !(item1 == item2); 
     } 
    } 
} 
0

Biến thể nhẹ của biểu mẫu đã được đăng bởi nhiều người khác ...

using System; 
... 
public override bool Equals (object obj) { 
    return Equals(obj as SomeClass); 
} 

public bool Equals (SomeClass someInstance) { 
    return Object.ReferenceEquals(this, someInstance) 
     || (!Object.ReferenceEquals(someInstance, null) 
      && this.Value == someInstance.Value); 
} 

public static bool operator ==(SomeClass lhs, SomeClass rhs) { 
    if(Object.ReferenceEquals(lhs, null)) { 
     return Object.ReferenceEquals(rhs, null); 
    } 
    return lhs.Equals(rhs); 
    //OR 
    return Object.ReferenceEquals(lhs, rhs) 
      || (!Object.ReferenceEquals(lhs, null) 
       && !Object.ReferenceEquals(rhs, null) 
       && lhs.Value == rhs.Value); 
} 

public static bool operator !=(SomeClass lhs, SomeClass rhs) { 
    return !(lhs == rhs); 
    // OR 
    return (Object.ReferenceEquals(lhs, null) || !lhs.Equals(rhs)) 
      && !Object.ReferenceEquals(lhs, rhs); 
} 

Cố gắng tìm một cách để thực hiện điều hành == sử dụng Equals để tránh sao chép logic so sánh giá trị ... mà không cần bất kỳ kiểm tra dư thừa (ReferenceEquals gọi w/các thông số tương tự) hoặc xét nghiệm không cần thiết (điều này có thể không null trong phương thức instance.Equals) và không có bất kỳ điều kiện rõ ràng nào ("ifs"). Nhiều hơn một lời trêu ghẹo tâm trí hơn bất cứ điều gì hữu ích.

gần nhất tôi có thể nghĩ ra được điều này, nhưng nó cảm thấy như nó phải là có thể mà không một phương pháp thêm :)

public bool Equals (SomeClass someInstance) { 
    return Object.ReferenceEquals(this, someInstance) 
     || (!Object.ReferenceEquals(someInstance, null) && EqualsNonNullInstance(someInstance); 
} 

public static bool operator ==(SomeClass lhs, SomeClass rhs) { 
    return Object.ReferenceEquals(lhs, rhs) 
    || (!Object.ReferenceEquals(lhs, null) && !Object.ReferenceEquals(rhs, null) && lhs.EqualsNonNullInstance(rhs)); 
} 

//super fragile method which returns logical non-sense 
protected virtual bool EqualsNonNullInstance (SomeClass someInstance) { 
    //In practice this would be a more complex method... 
    return this.Value == someInstance.Value; 
} 

Nhớ cách tẻ nhạt và dễ bị lỗi này tất cả là (tôi gần như chắc chắn có một lỗi trong mã trên ... mà vẫn còn hút vì ai muốn phân lớp một loại chỉ để kiểm tra bình đẳng đơn giản hơn một chút?), tôi nghĩ tôi sẽ tạo một số phương thức tĩnh xử lý tất cả các kiểm tra rỗng và chấp nhận một đại biểu hoặc yêu cầu và giao diện để thực hiện so sánh các giá trị (phần duy nhất thực sự thay đổi Type to Type).

Thật tuyệt vời nếu chúng ta chỉ có thể thêm thuộc tính vào các trường/thuộc tính/phương thức cần so sánh và để trình biên dịch/thời gian chạy xử lý tất cả các tedium.

Cũng đảm bảo các giá trị GetHashCode() bằng nhau cho bất kỳ trường hợp nào trong đó .Equals (đối tượng) trả về true hoặc crazy shit có thể xảy ra.

0

Ngoài ra còn có một plugin Fody Equals.Fody mà tạo ra Equals() và GetHashCode() tự động

Các vấn đề liên quan