c # Как найти, если два объекта равны - PullRequest
29 голосов
/ 27 мая 2010

Мне нужно знать лучший способ сравнить два объекта и выяснить, равны ли они. Я переопределяю как GethashCode, так и Equals. Итак, базовый класс выглядит так:

public class Test
{
    public int Value { get; set; }
    public string String1 { get; set; }
    public string String2 { get; set; }

    public override int GetHashCode()
    {
        return Value ^ String1.GetHashCode() ^ String2.GetHashCode();
    }

    public override bool Equals( object obj )
    {
        return GetHashCode() == obj.GetHashCode();
    }
}

Итак, для целей тестирования я создал два объекта:

Test t = new Test()
{
    Value = 1,
    String1 ="One",
    String2 = "One"
};

Test t2 = new Test()
{
    Value = 1,
    String1 = "Two",
    String2 = "Two"
};

bool areEqual = t.Equals( t2 );

При тестировании это areEqual возвращает истинное событие, хотя оба объекта различны. Я понимаю, что это потому, что String1 и String2 имеют одинаковое значение в каждом объекте и, таким образом, взаимно исключают друг друга при хешировании

Есть ли лучший способ избавиться от хэширования объекта, чем мой метод, который решит мою проблему?

Ответы [ 6 ]

39 голосов
/ 27 мая 2010

Ваш текущий метод равенства не работает - значений больше, чем возможных хеш-кодов. Вполне разумно (и ожидается), что иногда вы будете иметь значения, которые являются неравными, но дают тот же хеш. Равные должны проверить фактические значения :

public override bool Equals(object obj)
{
    Test test = obj as Test;
    if (obj == null)
    {
        return false;
    }
    return Value == test.Value &&
        String1 == test.String1 &&
        String2 == test.String2;
}

Несколько замечаний:

  • Ваш способ генерации хеш-кода даст одинаковое значение для любого фиксированного Value, если String1 и String2 одинаковы; оно также взорвется, если String1 или String2 равно нулю. Это неудачный аспект использования XOR для хеширования. Я предпочитаю что-то вроде этого:

    // Put this extension method in a utility class somewhere
    public static int SafeGetHashCode<T>(this T value) where T : class
    {
        return value == null ? 0 : value.GetHashCode();
    }
    
    // and this in your actual class
    public override int GetHashCode()
    {
        int hash = 19;
        hash = hash * 31 + Value;
        hash = hash * 31 + String1.SafeGetHashCode();
        hash = hash * 31 + String2.SafeGetHashCode();
        return hash;
    }
    
  • Вообще говоря, равенство становится сложным, когда наследование включается. Вы можете подумать о закрытии своего класса.

  • Вы также можете реализовать IEquatable<Test>

16 голосов
/ 27 мая 2010

Ваш Equals неверен - это должно определить , что означает, что две вещи равны - и наличие одинакового хеш-кода означает не означает равенство (однако другой хеш-код означает означает неравенство). Если «равенство» означает «обе строки попарно равны», то test that.

Re лучший хэш; xor печально известен этим, так как тривиально получить 0 с помощью xor значения вместе с самим собой. Лучший подход может быть что-то вроде:

int i = 0x65407627;
i = (i * -1521134295) + Value.GetHashCode();
i = (i * -1521134295) + (String1 == null ? 0 : String1.GetHashCode());
i = (i * -1521134295) + (String2 == null ? 0 : String2.GetHashCode());
return i;
4 голосов
/ 26 октября 2012

простой

Object.Equals(obj1, obj2);
2 голосов
/ 27 мая 2010

Для любых двух объектов равенство объектов подразумевает равенство хеш-кода, однако , равенство хеш-кода не подразумевает равенство объектов. С Object.GetHashCode на MSDN:

Хеш-функция должна иметь следующие свойства:

Если два объекта сравниваются как равные, Метод GetHashCode для каждого объекта должен вернуть то же значение. Тем не мение, если два объекта не сравниваются как равны, методы GetHashCode для два объекта не должны возвращаться разные значения.

Другими словами, ваш Equals написан неправильно. Это должно быть что-то вроде:

public override bool Equals(object obj)
{
    Test other = obj as Test;
    if (other == null)
        return false;

    return (Value == other.Value)
        && (String1 == other.String1)
        && (String2 == other.String2);
}

GetHashCode подходит для коллекций (например, Dictionary<K, V>) для быстрого определения приблизительного равенства. Equals для сравнения, если два объекта действительно одинаковы.

1 голос
/ 04 октября 2013

Вы можете сериализовать два объекта в JSON, а затем сравнить две строки, чтобы увидеть, совпадают ли они.

Например

JavaSriptSerializer serialiser = new JavaScriptSerializer();

string t1String = serialiser.Serialize(t);
string t2String = serialiser.Serialize(t2);

if(t1String == t2String)
   return true; //they are equal
else
   return false;
0 голосов
/ 27 мая 2010

не будет ли функция Equals всегда проверяется только с одним и тем же типом, не так ли:

//override
    public bool Equals(Test other)//(object obj) 
    {
        //return GetHashCode() == obj.GetHashCode();
        return (Value == other.Value) &&
               (String1 == other.String1) &&
               (String2 == other.String2);
    }

С уважением К сожалению

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...