2011-08-13 11 views
10

To jest przykład tego, co Próbuję zrobić:Jak mogę poprawić ten kod: dziedziczenia i 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; 
    } 
} 

Ale jak widać powyżej, będę musiał korzystać z wielu warunkowe "jeśli", aby zidentyfikować prawdziwy typ. Problem polega na tym, że muszę użyć klasy bazowej. Na przykład:

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

foo.Equals(another); 
+0

Dlaczego potrzebujesz "IEquatable " w pierwszej kolejności? Nie dodaje tutaj żadnej wartości. Po prostu upewnij się, że podtypy (sensownie) zastępują również 'object.GetHashCode' oraz' object.Equals (object) ', oprócz istniejącej implementacji' IEquatable '. Wtedy otrzymasz bezpłatną wysyłkę metod wirtualnych i będzie działać w wielu innych sytuacjach. – Ani

+0

@Ani Czy pokażesz mi przykład? –

+0

Hmm. Te zajęcia wydają się dziwne. Jaki jest cel Foo, ponieważ nie ma on właściwości? Również jeśli B jest po prostu A z jeszcze jedną liczbą, dlaczego nie B dziedziczy z A i po prostu dodaje Number3 zamiast z Foo? – alun

Odpowiedz

1

Spróbuj ten kawałek kodu:

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; 
    } 
} 

Uwaga: Można użyć funkcji Assert zrobić typechecking.

+0

Wygląda dobrze, na samym początku myślałem przy ustalaniu klasy Foo, jak streszczenie. Podoba mi się to, wydaje się dziwne wirtualne w klasie Foo, czy można zastąpić klasę Foo interfejsem? –

+0

Dalej, wybór należy do ciebie. Właśnie dostarczyłem ci rozwiązanie ... – RockWorld

3

Jako bezpośrednią odpowiedź na twoje pytanie, wydajesz się implementować IEquatable<Foo>, odraczając zawsze implementację (konkretną) podklasy: IEquatable<self>. To będzie wyglądać następująco:

(zły kod, tylko dla demonstracji)

// 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 }); 
} 

Ale naprawdę nie jest jasne, dlaczego trzeba porównań równości, aby zawsze iść „do” baza-klasa na Implementacja IEquatable<self>.

Framework ma już wirtualną metodę Equals, która spowoduje wysyłanie wywołań równości do odpowiedniej metody. Ponadto, EqualityComparar<T>.Default (który jest używany przez większość typów kolekcji do sprawdzania równości) już ma spryt, aby wybrać odpowiednio IEquatable<self>.Equals(self) lub object.Equals(object).

Próba stworzenia implementacji równości w klasie bazowej, która tylko przesyła dalej żądanie, dodaje do dowolnej wartości bez wartości, o ile widzę.

Bez dalszych wyjaśnień na temat , dlaczego potrzebujesz implementacji klasy podstawowej IEquatable<>, polecam po prostu wdrożenie równości poprawnie na każdym typie. Na przykład:

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

Myślę, że przegapiłeś połączenie z generic Equals w swoim nietypowym odpowiedniku – alun

+0

@alun: Ups, thanks. Naprawiony. – Ani

0

Jedną z opcji jest przeniesienie własności liczba1 i liczba2 do klasy bazowej, a jedynie porównać element dodany do podklasy w metodach równości podklas.

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

Uważam, że klasy pochodne nie powinny być obsługiwane w klasach bazowych. Zwykle "Foo" nic nie wie o A i B.

Nadal można uczynić podstawową implementację IEquatable wirtualną, pozwalając A i B na jej nadpisanie i wykonanie ich specyficznych sprawdzeń równości, nawet jeśli zarówno sprawdzanie równości, jak i sprawdzana instancja są dostępne tylko jako "Foo" lub "Object".

To by traktować .Equals (Foo obj) jak bardziej specyficzna forma Object.Equals (Object obj).

Powiązane problemy