2010-12-14 2 views
8

У меня есть класс, который содержит некоторые члены строки, некоторые двойные элементы и некоторые объекты массива.Сравнение объектов

Я создаю два объекта этого класса, есть ли простой, эффективный способ сравнения этих объектов и сказать их равными? Какие-либо предложения?

Я знаю, как написать функцию сравнения, но это займет много времени.

+2

Каковы ваши условия при равных условиях? – BoltClock

+0

В принципе, все значения в этих членах строки, двойных членах и массиве должны быть одинаковыми! – siva

+1

Возможный дубликат http://stackoverflow.com/questions/506096/comparing-object-properties-in-c – RameshVel

ответ

11

Единственный способ, которым вы действительно можете это сделать, - переопределить bool Object.Equals(object other), чтобы вернуть true, когда выполняются условия для равенства, и в противном случае вернуть false. Вы также должны переопределить int Object.GetHashCode(), чтобы вернуть int из всех данных, которые вы рассматриваете при переопределении Equals().

Отметим, что в контракте для GetHashCode() указано, что возвращаемое значение должно быть равным для двух объектов, когда Equals() вернет true при их сравнении. Это означает, что return 0; является допустимой реализацией GetHashCode(), но это приведет к неэффективности, когда объекты вашего класса используются в качестве ключей словаря или хранятся в HashSet<T>.

Путь я реализую равенства, как это:

public class Foo : IEquatable<Foo> 
{ 
    public bool Equals(Foo other) 
    { 
     if (other == null) 
      return false; 

     if (other == this) 
      return true; // Same object reference. 

     // Compare this to other and return true/false as appropriate. 
    } 

    public override bool Equals(Object other) 
    { 
     return Equals(other as Foo); 
    } 

    public override int GetHashCode() 
    { 
     // Compute and return hash code. 
    } 
} 

Простой способ реализации GetHashCode() является XOR вместе хэш-коды всех данных вы считаете равенства в Equals(). Так что если, например, свойства вы сравниваете равенства являются string FirstName; string LastName; int Id;, ваша реализация может выглядеть следующим образом:

public override int GetHashCode() 
{ 
    return (FirstName != null ? FirstName.GetHashCode() : 0)^
     (LastName != null ? LastName.GetHashCode() : 0)^
     Id; // Primitives of <= 4 bytes are their own hash codes 
} 

Я обычно делать не переопределение операторов равенства, поскольку большую часть времени я обеспокоенный с равенством только для целей словарных ключей или коллекций. Я бы рассмотрел только переопределение операторов равенства, если вы скорее всего сделаете больше сравнений по значению, чем по ссылке, поскольку оно синтаксически менее подробное. Однако вы должны помнить об изменении всех мест, где вы используете == или != на вашем объекте (в том числе при реализации Equals()!) Использовать Object.ReferenceEquals() или лить оба операнда в object. Эта неприятная gotcha (которая может вызвать бесконечную рекурсию в вашем тесте равенства, если вы не внимательны) является одной из основных причин, по которым я редко переопределяю этих операторов.

1

Лучший ответ - реализовать IEquatable для вашего класса - это может быть не тот ответ, который вы хотите услышать, но это лучший способ реализовать эквивалентность значений в .NET.

Другой вариант вычисления уникальный хеш всех членов вашего класса, а затем делает значение сравнения с тем, но это еще больше работы, чем писать функцию сравнения;)

+0

Хэш-подход также склонен к ложным срабатываниям. – cdhowie

+0

Да, не рекомендую. – Aaronontheweb

9

«правильный» способ сделать это в .NET является реализация IEquatable интерфейс для вашего класса:

public class SomeClass : IEquatable<SomeClass> 
{ 
    public string Name { get; set; } 
    public double Value { get; set; } 
    public int[] NumberList { get; set; } 

    public bool Equals(SomeClass other) 
    { 
     // whatever your custom equality logic is 
     return other.Name == Name && 
      other.Value == Value && 
      other.NumberList == NumberList; 
    } 
} 

Однако, если вы действительно хотите сделать это правильно, this isn't all you should do.Вы также должны переопределить методы Equals (объект, объект) и GetHashCode (object), чтобы, независимо от того, как ваш код вызова сравнивает равенство (возможно, в словаре или, возможно, в некоторой коллекции с ограниченным набором символов), ваш код, а не ссылочный код, будет определяющим фактором:

public class SomeClass : IEquatable<SomeClass> 
{ 
    public string Name { get; set; } 
    public double Value { get; set; } 
    public int[] NumberList { get; set; } 

    /// <summary> 
    /// Explicitly implemented IEquatable method. 
    /// </summary> 
    public bool IEquatable<SomeClass>.Equals(SomeClass other) 
    { 
     return other.Name == Name && 
      other.Value == Value && 
      other.NumberList == NumberList; 
    } 

    public override bool Equals(object obj) 
    { 
     var other = obj as SomeClass; 
     if (other == null) 
      return false; 
     return ((IEquatable<SomeClass>)(this)).Equals(other); 
    } 

    public override int GetHashCode() 
    { 
     // Determine some consistent way of generating a hash code, such as... 
     return Name.GetHashCode()^Value.GetHashCode()^NumberList.GetHashCode(); 
    } 
} 
1

Так как это объекты, то я предполагаю, что вам придется переопределить метод Equals для объектов. В противном случае метод Equals даст вам одобрение только в том случае, если оба объекта ссылаются на один и тот же объект.

Я знаю, что это не тот ответ, который вы хотите. Но поскольку в вашем классе мало свойств, вы можете легко переопределить этот метод.

5

Просто провел весь день, написав метод расширения, зацикливая на свойства объекта с различными сложными битами логики, чтобы иметь дело с разным типом свойства и фактически получил это близко к хорошему, а затем в 16:55 меня осенило что если вы сериализация два объекта, вам просто нужно сравнить две строки ... Дух

так вот метод расширения простой сериализатору, что работает даже на словарях

public static class TExtensions 
    { 
     public static string Serialize<T>(this T thisT) 
     { 
      var serializer = new DataContractSerializer(thisT.GetType()); 
      using (var writer = new StringWriter()) 
      using (var stm = new XmlTextWriter(writer)) 
      { 
       serializer.WriteObject(stm, thisT); 
       return writer.ToString(); 
      } 
     } 
} 

Теперь ваш тест может быть простым как

Asset.AreEqual(objA.Serialise(), objB.Serialise()) 

Еще не прошли обширные испытания, но выглядят многообещающими и, что более важно, простыми. В любом случае, еще один полезный метод, который должен быть установлен в вашей утилите?

+0

Но если ваш объект содержит свойства строки, не будут ли верхние/нижние регистры нарушать сравнение? Я имею в виду, что «UnitedStates» будет неравнодушным к «UnitedStates». –