2009-04-28 16 views
6

Potrzebuję napisać ogólnej metody w klasie bazowej, która zaakceptowałaby 2 obiekty jako parametry i porównuje je dla równości.Porównywanie 2 niestandardowych obiektów - C#

Np

public abstract class BaseData 
{ 

    public bool AreEqual(object O1, object O2) 
    { 
    //Need to implement this 
    } 
} 

public class DataTypeOne : BaseData 
{ 
    public string Name; 
    public string Address; 
} 

public class DataTypeTwo : BaseData 
{ 
    public int CustId; 
    public string CustName; 
} 

Sposób AreEqual() przyjmie 2 przypadki DataTypeOne lub 2 przypadki DataTypeTwo.

Zgaduję, że muszę użyć Odbicia? Mogę użyć LINQ, jeśli może być bardziej czytelny/zwięzły.

EDYTOWANIE: Powodem, dla którego chciałbym wdrożyć tę metodę w klasie bazowej, jest ograniczenie projektu. Na klasach pochodnych pracuje duża liczba programistów. Wdrażając to w klasie bazowej, staram się mieć 1 mniej rzeczy, o które się martwią.

+0

Dlaczego nie zastąpisz Object.Equals? – Paco

+0

Dlaczego trzeba wdrożyć AreEqual w klasie bazowej (i dlaczego bez generycznych)? Jeśli AreEqual jest abstrakcyjne, a DataTypeOne i DataTypeTwo implementują AreEqual, to jest to bardziej przejrzyste rozwiązanie. Krótko mówiąc: jaki jest powód typowej metody AreEqual? – boj

Odpowiedz

13

(zakładając, że to, co chcesz porównać wszystkie pola z dwóch obiektów dla równości.)

Zwykle nie przeszkadza przy użyciu odbicia do tego, że po prostu porównać każde pole samodzielnie. Interfejs IEquatable<T> istnieje w tym celu, ale możesz również zastąpić Object.Equals() dla tych typów. Na przykład:

public class DataTypeTwo : BaseData, IEquatable<DataTypeTwo> 
{ 
    public int CustId; 
    public string CustName; 

    public override int GetHashCode() 
    { 
     return CustId^CustName.GetHashCode(); // or whatever 
    } 

    public override bool Equals(object other) 
    { 
     return this.Equals(other as DataTypeTwo); 
    } 

    public bool Equals(DataTypeTwo other) 
    { 
     return (other != null && 
       other.CustId == this.CustId && 
       other.CustName == this.CustName); 
    } 
} 

Należy także rozważyć, czy Twój typ sens jako struct zamiast. Typy wartości automatycznie porównują równość, wykonując porównanie pole po polu.

Zauważ, że przez przesłonięcie Equals, w zasadzie osiągasz to, co (jak mi się wydaje) starasz się osiągnąć przy pomocy schematu "Master equals method". Oznacza to, że osoby korzystające z DataTypeTwo będą w stanie przetestować w sposób naturalny równość, nie wiedząc nic specjalnego o swoim interfejsie API - po prostu będą używać Equals, podobnie jak w przypadku innych rzeczy.

EDYCJA: Podziękowania dla Jareda za przypomnienie mi o GetHashCode. Będziesz także chciał go przesłonić, aby zachować normalnie wyglądające zachowanie w hashtables, upewniając się, że dwa dowolne obiekty, które są "równe", również zwracają ten sam kod skrótu.

+1

Nadal potrzebujesz kodu źródłowego GetHashCode – JaredPar

+0

Dlaczego potrzebujesz GetHashCode, równa się niewystarczająca? –

+1

Nigdy nie zmieniaj klasy w strukturze, aby zmienić zachowanie związane z porównywaniem równości. Małe typy danych, takie jak współrzędne kartezjańskie, które mają zachowywać się w pewien sposób "według wartości", są w porządku; ale struktury danych, które inny programista zakłada, że ​​są klasą, nie powinny być przekształcane w struktury do porównywania pod względem wartości - będziesz także niewidzialnie zmieniać zachowanie. inicjalizacja i (co najważniejsze) przypisanie. – perfectionist

0

Tak, będziesz musiał użyć odbicia, ponieważ klasa bazowa nic nie wie o klasach pochodnych. Ale dlaczego chcesz zaimplementować tę funkcję w klasie bazowej? Dlaczego nie w klasach pochodnych?

Ponadto istnieje standardowy sposób, aby to zrobić, przesłonięcie Object.GetHashCode() i Object.Equals().

1

to pewnie zrobić coś takiego:

public abstract class BaseData : IEquatable<BaseData> 
{ 
    public abstract bool Equals(BaseData other); 
} 

public class DataTypeOne : BaseData 
{ 
    public string Name; 
    public string Address; 

    public override bool Equals(BaseData other) 
    { 
     var o = other as DataTypeOne; 
     if(o == null) 
      return false; 
     return Name.Equals(o.Name) && Address.Equals(o.Address); 
    } 
} 

public class DataTypeTwo : BaseData 
{ 
    public int CustId; 
    public string CustName; 

    public override bool Equals(BaseData other) 
    { 
     var o = other as DataTypeTwo; 
     if (o == null) 
      return false; 
     return CustId == o.CustId && CustName.Equals(o.CustName); 
    } 
} 
+0

Być może wyjaśnienie, dlaczego jest "szalony" byłoby miłe. – madcolor

+0

Po prostu myślę, że nie ma potrzeby porównywania DataTypeOne i DataTypeTwo, ponieważ nigdy nie będą one równe. Innymi słowy, prawdopodobnie po prostu zaimplementowałem IEquatable w DataTypeOne i/lub IEquatable w DataTypeTwo. Lub najprawdopodobniej po prostu użyłbym linq lub czegoś podobnego, na przykład szukanie konkretnego DataTypeTwo z konkretnym CustId na przykład i nie zawracam sobie głowy IEquatable lub override Equals lub czymś podobnym. – Svish

4

Oto co wymyśliłem użyciu odbicia. Mam nadzieję, że to pomoże.

public bool AreEqual(object obj) 
    { 
     bool returnVal = true; 

     if (this.GetType() == obj.GetType()) 
     { 
      FieldInfo[] fields = this.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); 

      foreach (FieldInfo field in fields) 
      { 
       if(field.GetValue(this) != field.GetValue(obj)) 
       { 
        returnVal = false; 
        break; 
       } 
      } 
     } 
     else 
      returnVal = false; 

     return returnVal; 
    } 
+0

Dzięki Chrupiące, to było dokładnie to, czego szukałem! – Lanceomagnifico

2

Nie rób tego. Dziedziczenie to sposób na przejście, a każda klasa powinna w razie potrzeby zastąpić Equal i GetHashCode.

Być może dostaniesz pracę od tych twórców, ale wrócę, aby cię ugryźć w dupę w przyszłości, kiedy produkt musi być utrzymany.

Poważnie, po prostu spróbuj znaleźć inny sposób pomocy.

+1

+1, czasami jedyna poprawna odpowiedź na pytanie "Jak?" jest "Do not". – jwg

0
public void CompareTwoObjects() 
{ 
    try { 
     byte[] btArray = ObjectToByteArray(object1); //object1 is you custom object1 
     byte[] btArray2 = ObjectToByteArray(object2); //object2 is you custom object2 
     bool result = ByteArrayCompare(btArray, btArray2); 
    } catch (Exception ex) { 
     throw ex; 
    } 
} 

public byte[] ObjectToByteArray(object _Object) 
{ 
    try { 
     // create new memory stream 
     System.IO.MemoryStream _MemoryStream = new System.IO.MemoryStream(); 

     // create new BinaryFormatter 
     System.Runtime.Serialization.Formatters.Binary.BinaryFormatter _BinaryFormatter 
      = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 

     // Serializes an object, or graph of connected objects, to the given stream. 
     _BinaryFormatter.Serialize(_MemoryStream, _Object); 

     // convert stream to byte array and return 
     return _MemoryStream.ToArray(); 
    } catch (Exception _Exception) { 
     // Error 
     Console.WriteLine("Exception caught in process: {0}", _Exception.ToString()); 
    } 

    // Error occured, return null 
    return null; 
} 

public bool ByteArrayCompare(byte[] a1, byte[] a2) 
{ 
    if (a1.Length != a2.Length) 
     return false; 

    for (int i = 0; i < a1.Length; i++) 
     if (a1[i] != a2[i]) 
      return false; 

    return true; 
} 
+1

Dziękujemy za zamieszczenie odpowiedzi! Podczas gdy fragment kodu mógłby odpowiedzieć na pytanie, nadal dobrze jest dodać dodatkowe informacje, np. Wyjaśnić itp. – j0k

Powiązane problemy