2009-04-28 15 views
6

Ich muss eine generische Methode in der Basisklasse schreiben, die 2 Objekte als Parameter akzeptiert und vergleicht sie für die Gleichheit.Vergleichen von 2 benutzerdefinierten Objekten - C#

Ex:

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

Die AreEqual() Methode 2 Instanzen von DataTypeOne oder 2 Instanzen von DataTypeTwo akzeptieren würde.

Ich schätze, ich brauche Reflection zu verwenden? Ich kann LINQ verwenden, wenn es besser lesbar/prägnant ist.

EDIT: Der Grund, warum ich diese Methode in der Basisklasse implementieren möchte, ist aufgrund von Projekteinschränkungen. Es gibt eine große Anzahl von Entwicklern, die an den abgeleiteten Klassen arbeiten. Indem ich dies in der Basisklasse implementiere, versuche ich, eine weniger Sache zu haben, über die sie sich Sorgen machen müssen.

+0

Warum nicht Object.Equals außer Kraft setzen? – Paco

+0

Warum müssen Sie AreEqual in der Basisklasse implementieren (und warum ohne Generika)? Wenn AreEqual abstrakt ist und DataTypeOne und DataTypeTwo AreEqual implementieren, ist dies eine sauberere Lösung. Kurz gesagt: Was ist der Grund für die gemeinsame AreEqual-Methode? – boj

Antwort

13

(das Unter der Annahme, was Sie wollen, ist es, alle Felder der beiden Objekte auf Gleichheit zu vergleichen.)

Normalerweise würden Sie sich nicht die Mühe Reflexion für diese Verwendung, Sie würden nur jedes Feld selbst vergleichen. Die Schnittstelle IEquatable<T> existiert für diesen Zweck und Sie möchten möglicherweise auch Object.Equals() für die fraglichen Typen überschreiben. Zum Beispiel:

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

Überlegen Sie auch, ob Ihre Art stattdessen Sinn als struct macht. Werttypen vergleichen die Gleichheit automatisch, indem sie einen Feld-für-Feld-Vergleich durchführen.

Beachten Sie, dass Sie durch das Überschreiben Equals im Grunde erreichen, was (Sie scheint mir) Sie mit Ihrem "Master Equals-Methode" Schema zu erreichen versuchen. Das heißt, Leute, die DataTypeTwo verwenden, können natürlich auf Gleichheit prüfen, ohne etwas Besonderes über Ihre API wissen zu müssen - sie werden einfach Equals verwenden, als würden sie mit anderen Dingen arbeiten.

EDIT: Danke an Jared für die Erinnerung an GetHashCode. Sie sollten es auch überschreiben, um ein normal aussehendes Verhalten in Hashtabellen beizubehalten, indem Sie sicherstellen, dass zwei Objekte, die "gleich" sind, auch denselben Hash-Code zurückgeben.

+1

Sie benötigen ot noch überschreiben GetHashCode – JaredPar

+0

Warum Sie GetHashCode benötigen, ausreichend Equals nicht? –

+1

Bitte ändern Sie niemals eine Klasse in eine Struktur, um das Gleichheitsvergleichsverhalten zu ändern. Winzige Datentypen wie kartesische Koordinaten, von denen erwartet wird, dass sie sich in gewisser Weise "nach Wert" verhalten, sind in Ordnung; Aber Datenstrukturen, die von einem anderen Programmierer als Klasse angenommen werden, sollten nicht in Structs zum Vergleichen nach Wert umgewandelt werden - Sie werden auch unsichtbar das Verhalten ändern. Initialisierung und (vor allem) Zuweisung. – perfectionist

0

Ja, Sie müssen Reflektion verwenden, da die Basisklasse nichts über abgeleitete Klassen weiß. Aber warum möchten Sie diese Funktion in der Basisklasse implementieren? Warum nicht in den abgeleiteten Klassen?

Weiter gibt es eine Standardmethode, dies zu tun, indem Sie Object.GetHashCode() und Object.Equals() überschreiben.

1

Ich würde wahrscheinlich etwas tun:

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

Vielleicht wäre eine Erklärung, warum es "verrückt" ist, schön. – madcolor

+0

Nun, ich denke nur, dass es wirklich nicht notwendig ist, DataTypeOne und DataTypeTwo zu vergleichen, da sie niemals gleich sein werden. Mit anderen Worten würde ich wahrscheinlich implementieren nur IEquatable in DataTypeOne und/oder IEquatable in DataTypeTwo. Oder am ehesten würde ich nur Linq oder etwas das heißt verwenden suchen Sie nach einem particlar DataTypeTwo mit einem bestimmten CustId zum Beispiel und nicht die Mühe mit dem IEquatable oder überschreiben Equals oder so etwas. – Svish

4

Hier ist, was ich mit der Verwendung von Reflexion kam. Ich hoffe es hilft.

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

Danke Knusprig, das war * genau * wonach ich gesucht habe! – Lanceomagnifico

2

Tun Sie es nicht. Vererbung ist der Weg zu gehen und jede Klasse, sollte den Equal und GetHashCode, wo benötigt, überschreiben.

Vielleicht werden Sie etwas Arbeit von diesen Entwicklern jetzt getan, aber ich werde zurückkommen, um Sie in den Arsch in der Zukunft zu beißen, wenn das Produkt gewartet werden muss.

Im Ernst, nur versuchen, einen anderen Weg zu finden, zu helfen.

+1

+1, manchmal die einzige richtige Antwort auf "Wie?" ist "nicht". – 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

Danke für die Antwort! Während ein Code-Snippet die Frage beantworten könnte, ist es immer noch großartig, Zusatzinformationen wie Erklärungen usw. hinzuzufügen. – j0k

Verwandte Themen