Избегайте уравновешенного и моющегося шаблона, Swift 4.2 - PullRequest
1 голос
/ 11 июня 2019

В проекте мы используем классы для слоя модели, и поэтому я должен написать код, подобный этому:

// MARK: - Hashable
extension Player: Hashable {
    static func == (lhs: Player, rhs: Player) -> Bool {
        return lhs.hashValue == rhs.hashValue
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(self.name)
    }
}

Можно ли как-нибудь избежать этого шаблона? Возможно ли реализовать это Equatable сравнение по .hashValue по умолчанию? Спасибо.

Ответы [ 2 ]

4 голосов
/ 11 июня 2019

Это неправильно, и не имеет смысла, что компилятор синтезирует его автоматически:

static func == (lhs: Player, rhs: Player) -> Bool {
    return lhs.hashValue == rhs.hashValue
}

Идентичные объекты должны иметь одинаковое значение хеш-функции, но не наоборот: Отдельные объекты может иметь одно и то же значение хеш-функции.

Конкретно, в вашем примере: имя является строкой и существует бесконечно много разных строк, но только 2 64 разных значений хеш-функции.Таким образом, должно быть две разные строки с одинаковым значением хеш-функции.

Если все сохраненные свойства Hashable, то компилятор может полностью синтезировать соответствие для вас.Например,

struct Player : Equatable, Hashable {
    let name: String
    var score: Int
}

Здесь два игрока «идентичны», если у них одинаковое имя и одинаковый счет.

Если есть свойства, которые не могут быть изменены, или если вы хотите настроитьконцепция идентичности, то вы должны переопределить == и hash(into) соответственно.Хэш-функция должна использовать те же свойства, которые определяют идентичность в ==.Например,

struct Player : Equatable, Hashable {
    let name: String
    var score: Int

    static func == (lhs: Player, rhs: Player) -> Bool {
        return lhs.name == rhs.name
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(self.name)
    }
}

Теперь два игрока «идентичны», если у них одинаковое имя.

0 голосов
/ 11 июня 2019

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

Или использовать существующие решения (AutoEquatable, AutoHashable Sourcery шаблоны).

А также выМожно написать что-то вроде этого:

protocol IHash: class { }

extension IHash where Self: Hashable {
    static func ==(lhs: Self, rhs: Self) -> Bool {
        return lhs.hashValue == rhs.hashValue
    }
}

class User: IHash, Hashable {
    var name: String = ""

    func hash(into hasher: inout Hasher) {
        hasher.combine(self.name)
    }
}

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

...