如何根据实现该协议的两个实例的身份为一个协议实现 Equatable 协议?

Posted

技术标签:

【中文标题】如何根据实现该协议的两个实例的身份为一个协议实现 Equatable 协议?【英文标题】:How to implement Equatable protocol for a protocol based on the identity of two instances that implement this protocol? 【发布时间】:2018-01-18 15:10:16 【问题描述】:

我正在尝试为基于左右操作数标识的协议实现 Equatable 协议。换句话说:我如何为一个协议实现 Equatable 协议,以确定实现该协议的两个实例(在我的情况下为iNetworkSubscriber)是否相同(相同的对象引用)。像这样(错误信息包含在下面的代码中):

protocol iNetworkSubscriber : Equatable 

    func onMessage(_ packet: NetworkPacket)



func ==(lhs: iNetworkSubscriber, rhs: iNetworkSubscriber) -> Bool      // <- Protocol 'iNetworkSubscriber' can only be used as a generic constraint because it has Self or associated type requirements
    return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)               // <- Cannot invoke initializer for type 'ObjectIdentifier' with an argument list of type '(iNetworkSubscriber)'

...我也尝试过使用身份运算符本身:

func ==(lhs: iNetworkSubscriber, rhs: iNetworkSubscriber) -> Bool      // <- Protocol 'iNetworkSubscriber' can only be used as a generic constraint because it has Self or associated type requirements
    return lhs === rhs                                                  // <- Binary operator '===' cannot be applied to two 'iNetworkSubscriber' operands

有人知道如何解决这个问题吗?

【问题讨论】:

ObjectIdentifier needed for Swift equality?的可能重复 不幸的是,不是... ObjectIdentifier 在另一个线程中讨论了classes,我需要它作为protocol。我的目标是确定实现iNetworkSubscriber 协议的两个实例是否实际上是相同的实例。任何想法如何实现这一目标? 【参考方案1】:

这里有两个问题。首先是你不能在值类型上使用ObjectIdentifier。所以你必须声明这个协议需要引用(类)类型:

protocol NetworkSubscriber : class, Equatable 
    func onMessage(_ packet: NetworkPacket)

(请不要在协议的开头添加小写的i。这在 Swift 中会以多种方式造成混淆。)

那么,您不能将此协议用作类型。它描述了一种类型(因为它通过Equatable 依赖于Self)。所以接受它的函数必须是泛型的。

func ==<T: NetworkSubscriber>(lhs: T, rhs: T) -> Bool 
    return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)

鉴于NetworkSubscriber 必须是一个类,您应该非常仔细地询问您是否应该在这里使用继承而不是协议。具有关联类型的协议使用起来非常复杂,混合类和协议会产生更大的复杂性。如果您已经在使用类,则类继承会简单得多。

【讨论】:

您能谈谈具有相关类型的协议吗?我认为你可以很好地为我们分解它:D 感谢 Rob 非常全面的回答!我看到我需要重新打开 Swift 教科书并更深入地研究协议部分。我尝试采用我习惯使用的 C#/Java 模式。因此,我还在协议(接口)前面加上了“i”。但我现在意识到 Swift 协议!= C#/Java 接口 ;-) 我会选择继承方法。 我不久前就协议做了一次演讲。它仍然相当相关,尽管我今天可能会说不同的话,尤其是关于推动人们重返课堂;它们在我们今天拥有的 Swift 中工作得更好。 youtube.com/watch?v=QCxkaTj7QJs 关于 Swift 协议与 C# 接口不同的说法非常正确。特别是,您不应该使用协议,而且绝对不是具有关联类型的协议,只是为了使您的代码通用。 PAT 是在 stdlib 中有意义的复杂而强大的工具,但我发现在应用程序级代码中很少有意义。每次我看到一个关于 PAT 的 SO 问题时,我都会问“你真的需要 PAT,还是你只是想‘通用’,因为你认为你应该这样做?”大多数情况下,手头的实际程序没有必要这样做。【参考方案2】:

身份比较只对对象有意义(类的实例,class 协议)。所以马上,你知道你的协议需要一个class 约束:

protocol NetworkSubscriber: class, Equatable 
    func onMessage(_ packet: NetworkPacket)

一旦你这样做了,身份比较运算符=== 就可用于NetworkSubscriber 的实例(因为它们现在保证是对象)。我建议您直接使用===,而不是定义一个调用=====,以明确表明您正在执行身份比较,而不是值比较:

let ns1 = getNewNetworkSubscriber()
let ns2 = getNewNetworkSubscriber()
print(n1 === n2) // false
print(n1 === n1) // true

【讨论】:

感谢您的进一步解释。 @salocinx 我忘了说:如果你需要Equatable 的一致性,你仍然可以实现==,如果可能的话,我只会基于价值。 ===== 中有点粗略

以上是关于如何根据实现该协议的两个实例的身份为一个协议实现 Equatable 协议?的主要内容,如果未能解决你的问题,请参考以下文章

计算机网络学习:网络协议与标准 (未完)

(实用篇)php通过会话控制实现身份验证实例

根据原型链实现instanceof

利用STP生成树协议实现负载均衡

IPv4如何实现IP报文的分片和重组

基于STM8的IIC协议--实例篇--时钟模块(DS3231)读取