2011-08-13 25 views
10

Đây là một ví dụ về những gì Tôi đang cố gắng để làm:Làm thế nào tôi có thể cải thiện mã này: Thừa kế và IEquatable <>

public class Foo : IEquatable<Foo> 
{ 
    public bool Equals(Foo other) 
    { 
     Type type1 = this.GetType(); 
     Type type2 = other.GetType(); 

     if (type1 != type2) 
      return false; 

     if (type1 == typeof(A)) 
     { 
      A a = (A)this; 
      A b = (A)other; 

      return a.Equals(b); 
     } 
     else if (type1 == typeof(B)) 
     { 
      B c = (B)this; 
      B d = (B)other; 

      return c.Equals(d); 
     } 
     else 
     { 
      throw new Exception("Something is wrong"); 
     } 
    } 
} 

public class A : Foo, IEquatable<A> 
{ 
    public int Number1 { get; set; } 
    public int Number2 { get; set; } 

    public bool Equals(A other) 
    { 
     return this.Number1 == other.Number1 && this.Number2 == other.Number2; 
    } 
} 

public class B : Foo, IEquatable<B> 
{ 
    public int Number1 { get; set; } 
    public int Number2 { get; set; } 
    public int Number3 { get; set; } 

    public bool Equals(B other) 
    { 
     return this.Number1 == other.Number1 && this.Number2 == other.Number2 && this.Number3 == other.Number3; 
    } 
} 

Nhưng như bạn thấy ở trên, tôi sẽ phải sử dụng nhiều điều kiện 'if' để xác định loại thực. Vấn đề là tôi phải sử dụng lớp cơ sở. Ví dụ:

A a = new A(); 
Foo foo = a; 

foo.Equals(another); 
+0

Tại sao bạn cần 'IEquatable ' ngay từ đầu? Nó không thêm bất kỳ giá trị nào ở đây. Chỉ cần đảm bảo các kiểu con (hợp lý) ghi đè lên 'object.GetHashCode' và' object.Equals (object) ', ngoài việc thực hiện' IEquatable 'hiện có. Sau đó, bạn nhận được công văn phương pháp ảo miễn phí, và nó sẽ làm việc trong nhiều tình huống hơn. – Ani

+0

@Ani Bạn sẽ cho tôi xem một ví dụ? –

+0

Hmm. Những lớp học này có vẻ kỳ lạ. Mục đích của Foo là gì vì nó không có tài sản? Ngoài ra nếu B chỉ là một với một số nhiều lý do tại sao không B kế thừa từ A và chỉ cần thêm Number3 thay vì từ Foo? – alun

Trả lời

1

Hãy thử đoạn mã này:

public class Foo : IEquatable<Foo> 
{ 
    public virtual bool Equals(Foo other) 
    { 
     return true; 
    } 
} 

public class A : Foo,IEquatable<A> 
{ 
    public int Number1 { get; set; } 
    public int Number2 { get; set; } 

    public override bool Equals(Foo other) 
    { 
     if (other.GetType() == typeof(A)) 
     { 
      return Equals((A)other);     
     } 
     throw new InvalidOperationException("Object is not of type A"); 
    } 
    public bool Equals(A other) 
    { 
     return this.Number1 == other.Number1 && this.Number2 == other.Number2; 
    } 
} 

public class B : Foo,IEquatable<B> 
{ 
    public int Number1 { get; set; } 
    public int Number2 { get; set; } 
    public int Number3 { get; set; } 

    public override bool Equals(Foo other) 
    { 
     if (other.GetType() == typeof(B)) 
     { 
      return Equals((B)other); 

     } 
     throw new InvalidOperationException("Object is not of type B"); 
    } 
    public bool Equals(B other) 
    { 
     return this.Number1 == other.Number1 && this.Number2 == other.Number2 && this.Number3 == other.Number3; 
    } 
} 

Lưu ý: Bạn có thể sử dụng chức năng Khẳng định để làm typechecking.

+0

Nó có vẻ tốt, tại beggining tôi đã suy nghĩ trong việc thiết lập các lớp Foo như trừu tượng. Tôi thích cách này, có vẻ ảo lạ trong lớp Foo, có thể thay thế lớp Foo thành giao diện không? –

+0

Hãy tiếp tục, lựa chọn là của bạn, tôi chỉ cung cấp cho bạn giải pháp .... – RockWorld

3

Là câu trả lời trực tiếp cho câu hỏi của bạn, bạn có vẻ thực hiện IEquatable<Foo> bằng cách luôn trì hoãn việc triển khai thực hiện IEquatable<self> của lớp bê tông (cụ thể). Điều này sẽ giống như thế:

(Xấu mã, chỉ trình diễn)

// You need to specify what you want when this method is called on a 
// vanilla Foo object. I assume here that Foo is abstract. If not, please 
// specify desired behaviour. 
public bool Equals(Foo other) 
{ 
    if (other == null || other.GetType() != GetType()) 
     return false; 

    // You can cache this MethodInfo.. 
    var equalsMethod = typeof(IEquatable<>).MakeGenericType(GetType()) 
              .GetMethod("Equals"); 

    return (bool)equalsMethod.Invoke(this, new object[] { other }); 
} 

Nhưng nó thực sự không rõ ràng lý do tại sao bạn cần so sánh bình đẳng luôn đi "thông qua" các cơ sở hạng của IEquatable<self> triển khai.

Khuôn khổ đã có phương thức Equals ảo sẽ dẫn đến việc gửi các cuộc gọi bình đẳng đến phương thức thích hợp. Ngoài ra, EqualityComparar<T>.Default (được sử dụng bởi hầu hết các loại bộ sưu tập để thực hiện kiểm tra bình đẳng) đã có các tính năng để chọn IEquatable<self>.Equals(self) hoặc object.Equals(object) khi thích hợp.

Cố gắng tạo một triển khai bình đẳng trong lớp cơ sở chỉ chuyển tiếp yêu cầu thêm không có giá trị vào bất kỳ thứ gì, theo như tôi thấy.

Không giải thích thêm về lý do tại sao bạn cần thực hiện cơ sở IEquatable<>, tôi khuyên bạn chỉ nên thực hiện bình đẳng đúng trên mỗi loại. Ví dụ:

public class A : Foo, IEquatable<A> 
{ 
    public int Number1 { get; set; } 
    public int Number2 { get; set; } 

    public bool Equals(A other) 
    { 
     return other != null 
      && Number1 == other.Number1 
      && Number2 == other.Number2; 
    } 

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

    public override int GetHashCode() 
    { 
     return Number1^Number2; 
    } 
} 
+0

Tôi nghĩ rằng bạn đã bỏ lỡ một cuộc gọi đến Equals chung trong số không chung chung của bạn – alun

+0

@alun: Rất tiếc, cảm ơn. Đã sửa. – Ani

0

Một tùy chọn là di chuyển thuộc tính Number1 và Number2 đến lớp cơ sở và chỉ so sánh thành viên được thêm vào phân lớp trong phương thức bình đẳng của lớp con.

class Foo 
{ 
    // move the common properties to the base class 
    public int Number1 { get; set; } 
    public int Number2 { get; set; } 

    public override bool Equals(object obj) 
    { 
     Foo objfoo = obj as Foo; 
     return 
      objfoo != null 
      // require objects being compared to be of 
      // the same derived type (optionally) 
      && this.GetType() == obj.GetType() 
      && objfoo.Number1 == this.Number1 
      && objfoo.Number2 == this.Number2; 
    } 
    public override int GetHashCode() 
    { 
     // xor the hash codes of the elements used to evaluate 
     // equality 
     return Number1.GetHashCode()^Number2.GetHashCode(); 
    } 
} 

class A : Foo, IEquatable<A> 
{ 
    // A has no properties Foo does not. Simply implement 
    // IEquatable<A> 

    public bool Equals(A other) 
    { 
     return this.Equals(other); 
    } 

    // can optionally override Equals(object) and GetHashCode() 
    // to call base methods here 
} 

class B : Foo, IEquatable<B> 
{ 
    // Add property Number3 to B 
    public int Number3 { get; set; } 
    public bool Equals(B other) 
    { 
     // base.Equals(other) evaluates Number1 and Number2 
     return base.Equals(other) 
      && this.Number3 == other.Number3; 
    } 
    public override int GetHashCode() 
    { 
     // include Number3 in the hashcode, since it is used 
     // to evaluate equality 
     return base.GetHashCode()^Number3.GetHashCode(); 
    } 
    public override bool Equals(object obj) 
    { 
     return this.Equals(obj as B); 
    } 
} 
0

Tôi nghĩ rằng các lớp dẫn xuất không nên được xử lý trong các lớp cơ sở. Thông thường, "Foo" sẽ không biết gì về A và B.

Vẫn có thể thực hiện cơ sở triển khai IEquatable ảo, cho phép A và B ghi đè và thực hiện kiểm tra bình đẳng cụ thể của chúng, ngay cả khi kiểm tra bình đẳng và kiểm tra chỉ có sẵn dưới dạng "Foo" hoặc "Đối tượng".

Điều đó sẽ xử lý .Equals (Foo obj) giống như một dạng cụ thể hơn của Object.Equals (Object obj).

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