Sortedset не использует собственные равные

Мой класс реализует IEquatable и IComparable. Затем он добавляется в отсортированный набор.

Цель состоит в том, чтобы отсортировать его по свойству "Date" и сделать его равным, если "ID1" и "ID2" совпадают.

Реализованные методы:

public int CompareTo(MyClass other)
{
    return other.Date.CompareTo(Date);
}

public override int GetHashCode()
{
    unchecked
    {
        var hashCode = ID1;
        hashCode = (hashCode * 397) ^ ID2;
        return hashCode;
    }
}

public bool Equals(MyClass other)
{
    if (ReferenceEquals(null, other)) 
       return false;

    if (ReferenceEquals(this, other)) 
       return true;

    return ID1 == other.ID1
        && ID2 == other.ID2;
}

Результирующий отсортированный набор правильно отсортирован, но все еще есть элементы, которые должны быть равными и, следовательно, не в наборе. При использовании точек останова кажется, что ни GetHashCode, ни Equals не вызываются.

Любые советы о том, как решить эту проблему?

1 ответ

Решение

SortedSet использования CompareTo для сортировки и сравнения на равенство.

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

Если вам нужно такое поведение, возможно, самый простой способ сделать это - обернуть лежащую в основе коллекцию в ее собственный класс, который проверит отличимость перед добавлением новых элементов в коллекцию.

Вы также должны быть осторожны, чтобы элементы, которые равны для сортировки, но не равны по вашему Equals Метод может быть добавлен в базовую коллекцию. В вашем случае, где сортировка выполняется Date и равенство достигается ID1 а также ID2 это может выглядеть примерно так:

public int CompareTo(MyClass other)
{
    var result = other.Date.CompareTo(Date);
    if(result != 0)
        return result;
    result = other.ID1.CompareTo(ID1);
    if(result != 0)
        return result;
    return other.ID2.CompareTo(ID2);
}

Это налагает дополнительный порядок, если даты совпадают, но я не ожидаю, что это станет проблемой.

В качестве альтернативы, вы можете "обмануть", заставляя элементы, которые равны, сравнивать как равные в позиции сортировки. Это, вероятно, лучше всего перенести в обычай IComparerпотому что это не нормальное поведение сортировки, которое вы хотите, вне SortedSet:

public int CompareTo(MyClass other)
{
    if(other.Equals(this))
        return 0;

    var result = other.Date.CompareTo(Date);
    if(result != 0)
        return result;
    result = other.ID1.CompareTo(ID1);
    if(result != 0)
        return result;
    return other.ID2.CompareTo(ID2);
}

Это позволит вам избежать создания собственного класса-обертки вокруг коллекции и будет более безопасным. Это довольно хакерский, хотя.

Другие вопросы по тегам