111 votes

Comparaison des propriétés d'objet en c #

C'est ce que j'ai proposé comme méthode sur une classe héritée par beaucoup de mes autres classes. L'idée est qu'elle permet la comparaison simple entre les propriétés d'objets du même type.

Maintenant, cela fonctionne - mais dans l’intérêt d’améliorer la qualité de mon code, je pensais le jeter aux fins de vérification. Comment cela peut-il être meilleur / plus efficace / etc.?

 /// <summary>
/// Compare property values (as strings)
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public bool PropertiesEqual(object comparisonObject)
{

    Type sourceType = this.GetType();
    Type destinationType = comparisonObject.GetType();

    if (sourceType == destinationType)
    {
        PropertyInfo[] sourceProperties = sourceType.GetProperties();
        foreach (PropertyInfo pi in sourceProperties)
        {
            if ((sourceType.GetProperty(pi.Name).GetValue(this, null) == null && destinationType.GetProperty(pi.Name).GetValue(comparisonObject, null) == null))
            {
                // if both are null, don't try to compare  (throws exception)
            }
            else if (!(sourceType.GetProperty(pi.Name).GetValue(this, null).ToString() == destinationType.GetProperty(pi.Name).GetValue(comparisonObject, null).ToString()))
            {
                // only need one property to be different to fail Equals.
                return false;
            }
        }
    }
    else
    {
        throw new ArgumentException("Comparison object must be of the same type.","comparisonObject");
    }

    return true;
}
 

160voto

Big T Points 1951

Je cherchais un extrait de code qui ferait quelque chose de similaire pour aider à l'écriture du test unitaire. Voici ce que j'ai fini par utiliser.

 public static bool PublicInstancePropertiesEqual<T>(T self, T to, params string[] ignore) where T : class 
  {
     if (self != null && to != null)
     {
        Type type = typeof(T);
        List<string> ignoreList = new List<string>(ignore);
        foreach (System.Reflection.PropertyInfo pi in type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance))
        {
           if (!ignoreList.Contains(pi.Name))
           {
              object selfValue = type.GetProperty(pi.Name).GetValue(self, null);
              object toValue = type.GetProperty(pi.Name).GetValue(to, null);

              if (selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue)))
              {
                 return false;
              }
           }
        }
        return true;
     }
     return self == to;
  }
 

MODIFIER:

Même code que ci-dessus mais utilise les méthodes LINQ et Extension:

 public static bool PublicInstancePropertiesEqual<T>(this T self, T to, params string[] ignore) where T : class
{
    if (self != null && to != null)
    {
        var type = typeof(T);
        var ignoreList = new List<string>(ignore);
        var unequalProperties =
            from pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
            where !ignoreList.Contains(pi.Name)
            let selfValue = type.GetProperty(pi.Name).GetValue(self, null)
            let toValue = type.GetProperty(pi.Name).GetValue(to, null)
            where selfValue != toValue && (selfValue == null || !selfValue.Equals(toValue))
            select selfValue;
        return !unequalProperties.Any();
    }
    return self == to;
}
 

64voto

Liviu Trifoi Points 1479

Les limites que je vois dans votre code:

  • Le plus important est qu'il ne fait pas de profondeur de l'objet de la comparaison.

  • Il ne fait pas un élément par élément de comparaison dans le cas où les propriétés sont des listes ou qui contiennent des listes d'éléments (cela peut aller de la n-niveaux).

  • Il ne prend pas en compte le fait que certains type de propriétés ne doivent pas être comparés (par exemple, un Func des biens utilisés à des fins de filtrage, comme celui de la PagedCollectionView classe).

  • Il permet de ne pas garder trace de ce que les propriétés étaient vraiment différents (de sorte que vous pouvez montrer à vos affirmations).

J'étais à la recherche aujourd'hui une solution pour l'unité des fins de test à faire à la propriété par propriété comparaison en profondeur et j'ai fini par utiliser: http://comparenetobjects.codeplex.com.

C'est une bibliothèque libre avec une seule classe que vous pouvez utiliser simplement comme ceci:

var compareObjects = new CompareObjects()
{
    CompareChildren = true, //this turns deep compare one, otherwise it's shallow
    CompareFields = false,
    CompareReadOnly = true,
    ComparePrivateFields = false,
    ComparePrivateProperties = false,
    CompareProperties = true,
    MaxDifferences = 1,
    ElementsToIgnore = new List<string>() { "Filter" }
};

Assert.IsTrue(
    compareObjects.Compare(objectA, objectB), 
    compareObjects.DifferencesString
);

Aussi, il peut être facilement re-compilé pour Silverlight. Il suffit de copier une classe dans un projet Silverlight et retirez-en une ou deux lignes de code pour des comparaisons qui ne sont pas disponibles dans Silverlight, des membres de la comparaison.

6voto

Gishu Points 59012

Je pense qu'il serait mieux de suivre le modèle pour Remplacer l'Objet#Equals()
Pour une meilleure description: Lire le projet de Loi de Wagner Efficace C# - Item 9 je crois

public override Equals(object obOther)
{
  if (null == obOther)
    return false;
  if (object.ReferenceEquals(this, obOther)
    return true;
  if (this.GetType() != obOther.GetType())
    return false;
  # private method to compare members.
  return CompareMembers(this, obOther as ThisClass);
}
  • Aussi dans les méthodes qui permettent de vérifier l'égalité, vous devez retourner true ou false. soit ils sont égaux, ils ne sont pas.. au lieu de lancer une exception, retourne false.
  • Je considère primordial de l'Objet#Égal.
  • Même si vous devez avoir considéré ce, à l'aide de la Réflexion afin de comparer les propriétés est censé être lent (je n'ai pas de chiffres pour étayer cette). C'est le comportement par défaut pour valueType#Égal en C# et il est recommandé que vous remplacez est Égal pour les types de valeur et un membre sage de comparer les performances. (À l'heure, je à la vitesse de lire ce que vous avez une collection de Propriété personnalisée objets... mon mauvais.)

Mise À Jour-Décembre 2011:

  • Bien sûr, si le type a déjà une production Equals (), alors vous avez besoin d'une autre approche.
  • Si vous utilisez ce pour comparer immuable structures de données exclusivement à des fins de test, vous ne devez pas ajouter d'Égal à cours de production de (Quelqu'un pourrait tuyau de tests en chainging l'est Égal à la mise en œuvre ou vous pouvez empêcher la création de la production-requis est Égal à la mise en œuvre).

6voto

Edward Brey Points 8771

Si les performances importent peu, vous pouvez les sérialiser et comparer les résultats:

 var serializer = new XmlSerializer(typeof(TheObjectType));
StringWriter serialized1 = new StringWriter(), serialized2 = new StringWriter();
serializer.Serialize(serialized1, obj1);
serializer.Serialize(serialized2, obj2);
bool areEqual = serialized1.ToString() == serialized2.ToString();
 

4voto

thanei Points 31

J'ajouterais la ligne suivante à la méthode PublicInstancePropertiesEqual pour éviter les erreurs de copier-coller:

 Assert.AreNotSame(self, to);
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X