2009-04-28 9 views
6

J'ai besoin d'écrire une méthode générique dans la classe de base qui accepterait 2 objets comme paramètres et les compare pour l'égalité.Comparer 2 objets personnalisés - 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; 
} 

La méthode AreEqual() accepterait 2 cas de DataTypeOne ou 2 cas de DataTypeTwo.

Je suppose que j'ai besoin d'utiliser Reflection? Je peux utiliser LINQ si cela peut être plus lisible/concis.

EDIT: La raison pour laquelle je voudrais implémenter cette méthode dans la classe de base est due à des restrictions de projet. Il y a un grand nombre de développeurs travaillant sur les classes dérivées. En mettant cela en œuvre dans la classe de base, j'essaie d'avoir 1 chose de moins à leur sujet.

+0

Pourquoi ne surchargez-vous pas Object.Equals? – Paco

+0

Pourquoi avez-vous besoin d'implémenter AreEqual dans la classe de base (et pourquoi sans génériques)? Si AreEqual est abstrait et DataTypeOne et DataTypeTwo implémente AreEqual, il s'agit d'une solution plus propre. Donc, en bref: quelle est la raison de la méthode commune AreEqual? – boj

Répondre

13

(En supposant que ce que vous voulez est de comparer tous les champs des deux objets pour l'égalité).

Habituellement, vous ne se souciaient pas utiliser la réflexion pour cela, vous devriez juste comparer chaque champ vous-même. L'interface IEquatable<T> existe à cet effet et vous pouvez également remplacer Object.Equals() sur les types en question. Par exemple:

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

Aussi, tenez compte si oui ou non votre type logique comme struct à la place. Les types de valeur comparent automatiquement l'égalité en effectuant une comparaison champ par champ.

Notez qu'en surchargeant Equals, vous réalisez essentiellement ce que vous essayez de réaliser avec la méthode "master equals method" (me semble-t-il). Autrement dit, les personnes utilisant DataTypeTwo pourront tester naturellement l'égalité sans avoir à connaître quoi que ce soit de spécial au sujet de votre API - elles utiliseront simplement Equals comme elles le feraient avec d'autres choses.

EDIT: Merci à Jared de me rappeler à propos de GetHashCode. Vous devrez également le remplacer pour conserver un comportement normal dans les tables de hachage en vous assurant que deux objets "égaux" renvoient le même code de hachage.

+1

Vous avez toujours besoin de surcharger GetHashCode – JaredPar

+0

Pourquoi avez-vous besoin de GetHashCode, n'est pas suffisant? –

+1

Veuillez ne jamais changer une classe en structure pour le seul plaisir de changer le comportement de comparaison d'égalité. Les types de données minuscules comme les coordonnées cartésiennes qui sont censées se comporter d'une certaine façon "par valeur" sont bien; mais les structures de données qu'un autre programmeur supposera être une classe ne devraient pas être transformées en structures pour la comparaison par valeur - vous changerez également invisiblement le comportement par rapport à. initialisation et (surtout) affectation. – perfectionist

0

Oui, vous devrez utiliser la réflexion, car la classe de base ne connaît rien aux classes dérivées. Mais pourquoi voulez-vous implémenter cette fonction dans la classe de base? Pourquoi pas dans les classes dérivées?

En outre, il existe une manière standard de le faire en substituant Object.GetHashCode() et Object.Equals().

1

je ferais probablement quelque chose comme ceci:

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

Peut-être une explication de pourquoi c'est «fou» serait bien. – madcolor

+0

Eh bien, je pense juste qu'il n'est vraiment pas nécessaire de comparer DataTypeOne et DataTypeTwo, car ils ne seront jamais égaux. En d'autres termes, j'appliquerais probablement IEquatable dans DataTypeOne et/ou IEquatable dans DataTypeTwo. Ou plus probablement j'utiliserais simplement linq ou quelque chose pour trouver un DataTypeTwo particulier avec un CustId particulier par exemple et ne pas déranger avec le IEquatable ou remplacer Equals ou quelque chose comme ça. – Svish

4

Voici ce que je suis venu avec l'aide de la réflexion. J'espère que cela aide.

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

Merci Crispy, c'était * exactement * ce que je cherchais! – Lanceomagnifico

2

Ne le faites pas. L'héritage est le chemin à parcourir et chaque classe devrait remplacer les Equal et GetHashCode si nécessaire.

Peut-être que vous obtiendrez un travail fait de ces développeurs maintenant, mais je vais revenir à vous mordre dans le cul à l'avenir lorsque le besoin produit être maintenu.Sérieusement, essayez simplement de trouver un autre moyen d'aider.

+1

+1, parfois la seule réponse correcte à "Comment?" est "Ne pas". – 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

Merci d'avoir posté une réponse! Alors qu'un extrait de code pourrait répondre à la question, il est toujours bon d'ajouter des informations complémentaires, comme expliquer, etc. – j0k

Questions connexes