Сравнение двух протокольных экземпляров на равенство в Swift - PullRequest
0 голосов
/ 16 февраля 2020

Вот сделка,

Я пишу SDK и хочу объявить наблюдателей в качестве протоколов, а не классов или структур (это своего рода гибрид "Наблюдатель / Делегат").

Я хочу иметь возможность сравнивать два аргумента, которые передаются в качестве ссылок на протокол, в отличие от конкретных классов / структур, которыми они на самом деле являются, - IRL.

Я знаю, что «легкий» способ получить сравнение заключается в ограничении протоколов Hashable или Equatable, но я хочу избежать обременения пользователя (это SDK).

Вот небольшая площадка с тем, что я имею в виду:

protocol A {
    func AFunc() -> String
}

class APrime: A {
    func AFunc() -> String { "I AM GROOT" }
}

let variableA = APrime()
let variableB = APrime()

func compareTypes(_ inA: A, _ inB: A) -> String {
//    if inA == inB {
//        return ""
//    }
    return "not "
}

print("A is \(compareTypes(variableA, variableB))B.")

print("A is \(compareTypes(variableA, variableA))A.")

Слабый бит - это закомментированный раздел в compareTypes(_: A, _: A). Мне нужно выяснить, как их сравнивать, не заходя в «Хаксильванию», что я мог бы сделать, сравнивая адреса AFun c () в каждом случае.

Ожидаемый результат:

A is not B.
A is A.

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

1 Ответ

0 голосов
/ 01 марта 2020

Просто чтобы добавить к этому некоторое закрытие, вот как я решаю это:

protocol A {
    var uuid: Int { get } // This is the secret sauce. It will contain a unique UUID, associated with the instance.
    func AFunc() -> String
}

class APrime: A {
    let uuid: Int = Int.random(in: 0..<1000) // The UUID is initialized with the instance.
    func AFunc() -> String { "I AM GROOT" }
}

let variableA = APrime()
let variableB = APrime()
let variableC = variableA

func compareTypes(_ inA: A, _ inB: A) -> String {
    if inA.uuid == inB.uuid { // We compare UUIDs.
        return ""
    }
    return "not "
}

print("C is \(compareTypes(variableC, variableB))B.")

print("C is \(compareTypes(variableC, variableA))A.")

Переменная "uuid" обычно является действительным типом UUID , но я этого не сделал хочу импортировать Foundation в примере, поэтому я просто сделал простой ранд. Он получает смысл.

Это выводит:

C is not B.
C is A.

И есть другой способ (который я иногда использую):

protocol B {
    func BFunc() -> String
    func amIThisOne(_ instanceToCompare: B) -> Bool // This is an identity comparator
}

class BPrime: B {
    func BFunc() -> String { "I AM GROOT'S BROTHER" }
    // We compare ourselves against the other instance, assuming it can be cast to our own type.
    func amIThisOne(_ inInstanceToCompare: B) -> Bool {
        guard let instanceToCompare = inInstanceToCompare as? Self else { return false }
        return self === instanceToCompare
    }
}

let variableD = BPrime()
let variableE = BPrime()
let variableF = variableD

print("D is \(variableE.amIThisOne(variableD) ? "" : "not ")E.")

print("D is \(variableD.amIThisOne(variableF) ? "" : "not ")F.")

Какие выходы:

D is not E.
D is F.

Это позволяет более программным c способом сравнения экземпляров.

КАК ЭТО НЕ СДЕЛАТЬ

А потом, из Конечно, если у нас есть контроль над экземплярами, мы можем по-настоящему сделать Equatable (для этого требуется, чтобы игровая площадка импортировала Foundation):

protocol C: Equatable {
    func CFunc() -> String
}

class CPrime: C {
    // This is actually not what I want, as I want to compare protocols, not conforming classes.
    static func == (lhs: CPrime, rhs: CPrime) -> Bool {
        guard let lhs = lhs as? Self else { return false }
        guard let rhs = rhs as? Self else { return false }

        return lhs === rhs
    }

    func CFunc() -> String { "I AM GROOT'S UDDER BROTHER" }
}

let variableG = CPrime()
let variableH = CPrime()
let variableI = variableG

print("G is \(variableG == variableH ? "" : "not ")H.")

print("G is \(variableI == variableG ? "" : "not ")I.")

Какие выходы:

G is not H.
G is I.
...