如何根据实现该协议的两个实例的身份为一个协议实现 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 协议?的主要内容,如果未能解决你的问题,请参考以下文章