Swift扩展通用协议适配:如何让.set(to:)覆盖更多类型及排除条件?
问题1:让.set(to:)覆盖更多类型,无需重复实现
首先得说,你之前给Equatable加扩展其实是个误区——这个set(to:)方法根本不需要依赖Equatable协议!赋值操作variable = self只要求左右类型一致,和是否能比较完全无关。
要让这个方法支持所有类型,只需要去掉协议约束,直接给所有类型添加扩展就好:
extension Any { @discardableResult public func set(to variable: inout Self) -> Self { variable = self return self } }
或者更严谨的写法(效果和上面一致,因为所有类型都隐式遵循Any):
extension Self { @discardableResult public func set(to variable: inout Self) -> Self { variable = self return self } }
这样修改后,不管是遵循Equatable的基础类型(比如Int、String),还是不遵循的自定义类型(比如你自己写的未实现==的struct或class),都能调用.set(to:)方法,而且只需要实现一次,完全不会有重复提示的问题。
举个实际使用的例子:
// 自定义不遵循Equatable的类型 struct MyType { var value: Int } var myVar = MyType(value: 0) let newValue = MyType(value: 100) // 一行完成赋值并返回值 let returnedValue = newValue.set(to: &myVar) print(myVar.value) // 输出100 print(returnedValue.value) // 输出100
问题2:Swift扩展中是否支持排除式where子句?
很遗憾,在你使用的Swift 4.1(以及后续直到当前的Swift版本)中,不支持直接用!来写排除条件的where子句,比如你提到的extension Equatable where !(Element: CustomStringConvertible)这种语法是不合法的。
不过我们可以用间接方式实现类似“排除”的效果,利用Swift扩展的优先级规则:更具体的扩展会覆盖更通用的扩展。
举个例子,如果你想给所有Equatable类型加一个方法,但排除那些同时遵循CustomStringConvertible的类型,可以这么做:
- 先给所有
Equatable类型添加默认实现:
extension Equatable { func myMethod() { print("这是给所有Equatable类型的默认实现") } }
- 再给同时遵循
Equatable和CustomStringConvertible的类型添加一个空实现(或者你想要的特殊逻辑):
extension Equatable where Self: CustomStringConvertible { func myMethod() { // 空实现或自定义逻辑,会覆盖上面的默认实现 } }
这样一来,同时遵循两个协议的类型调用myMethod()时,会使用第二个扩展的实现;其他Equatable类型则会使用第一个扩展的实现,间接达到了“排除”CustomStringConvertible类型的效果。
内容的提问来源于stack exchange,提问作者Noah Wilder

