协议上的Swift Equatable [英] Swift Equatable on a protocol

查看:181
本文介绍了协议上的Swift Equatable的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不认为这可以解决,但是我还是会问.我有一个协议:

I don't think this can be done but I'll ask anyway. I have a protocol:

protocol X {}

还有一个班级

class Y:X {}

在我的其余代码中,我使用协议X引用所有内容.在该代码中,我希望能够执行以下操作:

In the rest of my code I refer to everything using the protocol X. In that code I would like to be able to do something like:

let a:X = ...
let b:X = ...
if a == b {...}

问题是,如果我尝试实现Equatable:

The problem is that if I try to implement Equatable:

protocol X: Equatable {}
func ==(lhs:X, hrs:X) -> Bool {
    if let l = lhs as? Y, let r = hrs as? Y {
        return l.something == r.something
    }
    return false
} 

尝试允许使用==的方法,同时将实现隐藏在协议的后面.

The idea to try and allow the use of == whilst hiding the implementations behind the protocol.

Swift并不喜欢这样,因为Equatable具有Self引用,它将不再允许我将其用作类型.仅作为通用参数.

Swift doesn't like this though because Equatable has Self references and it will no longer allow me to use it as a type. Only as a generic argument.

那么,有没有人找到一种将操作符应用于协议的方法,而又不会使该协议不能用作一种类型?

So has anyone found a way to apply an operator to a protocol without the protocol becoming unusable as a type?

推荐答案

如果直接在协议上实现Equatable,它将不再可用作类型,这违背了使用协议的目的.即使仅在不符合Equatable一致性的协议上实现==函数,结果也可能是错误的.有关这些问题的演示,请参见我的博客上的这篇文章:

If you directly implement Equatable on a protocol, it will not longer be usable as a type, which defeats the purpose of using a protocol. Even if you just implement == functions on protocols without Equatable conformance, results can be erroneous. See this post on my blog for a demonstration of these issues:

https://khawerkhaliq.com/blog/swift-protocols-equatable -part-one/

我发现最有效的方法是使用类型擦除.这允许对协议类型(包装在类型橡皮擦中)进行==比较.重要的是要注意,尽管我们继续在协议级别进行工作,但实际的==比较会委托给基础的具体类型,以确保获得正确的结果.

The approach that I have found to work best is to use type erasure. This allows making == comparisons for protocol types (wrapped in type erasers). It is important to note that while we continue to work at the protocol level, the actual == comparisons are delegated to the underlying concrete types to ensure correct results.

我使用您的简要示例构建了类型橡皮擦,并在末尾添加了一些测试代码.我在协议中添加了类型为String的常量,并创建了两个符合标准的类型(对于演示而言,结构是最简单的),以便能够测试各种情况.

I have built a type eraser using your brief example and added some test code at the end. I have added a constant of type String to the protocol and created two conforming types (structs are the easiest for demonstration purposes) to be able to test the various scenarios.

有关使用的类型擦除方法的详细说明,请查看上述博客文章的第二部分:

For a detailed explanation of the type erasure methodology used, check out part two of the above blog post:

https://khawerkhaliq.com/blog/swift-protocols-equatable -part-two/

下面的代码应支持您要实现的相等性比较.您只需要将协议类型包装在类型橡皮擦实例中即可.

The code below should support the equality comparison that you wanted to implement. You just have to wrap the protocol type in a type eraser instance.

protocol X {
    var name: String { get }
    func isEqualTo(_ other: X) -> Bool
    func asEquatable() -> AnyEquatableX
}

extension X where Self: Equatable {
    func isEqualTo(_ other: X) -> Bool {
        guard let otherX = other as? Self else { return false }
        return self == otherX
    }
    func asEquatable() -> AnyEquatableX {
        return AnyEquatableX(self)
    }
}

struct Y: X, Equatable {
    let name: String
    static func ==(lhs: Y, rhs: Y) -> Bool {
        return lhs.name == rhs.name
    }
}

struct Z: X, Equatable {
    let name: String
    static func ==(lhs: Z, rhs: Z) -> Bool {
        return lhs.name == rhs.name
    }
}

struct AnyEquatableX: X, Equatable {
    var name: String { return value.name }
    init(_ value: X) { self.value = value }
    private let value: X
    static func ==(lhs: AnyEquatableX, rhs: AnyEquatableX) -> Bool {
        return lhs.value.isEqualTo(rhs.value)
    }
}

// instances typed as the protocol
let y: X = Y(name: "My name")
let z: X = Z(name: "My name")
let equalY: X = Y(name: "My name")
let unequalY: X = Y(name: "Your name")

// equality tests
print(y.asEquatable() == z.asEquatable())           // prints false
print(y.asEquatable() == equalY.asEquatable())      // prints true
print(y.asEquatable() == unequalY.asEquatable())    // prints false

请注意,由于类型橡皮擦符合协议,因此可以在需要协议类型实例的任何地方使用类型橡皮擦的实例.

Note that since the type eraser conforms to the protocol, you can use instances of the type eraser anywhere an instance of the protocol type is expected.

希望这会有所帮助.

这篇关于协议上的Swift Equatable的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆