Swift是否支持协议的显式专属一致性?
首先说明:我知道难免会有类似问题,这里有一个相关问题。虽然主题相同,但其意图和使用场景不同,因此解决那个问题不等于实现我这里的需求。请先阅读我的问题再标记为重复,谢谢!
在Swift里,我们常用Protocol(协议),它和其他语言的Interface(接口)类似但又有区别。我在C#这类语言里见过同时支持隐式和显式实现接口的能力,想知道Swift有没有类似的显式实现协议的特性。
我用Swift语法写了一段伪代码来展示这个概念(这段代码无法编译,仅作示例):
protocol DateSortable{ var sortDate:Date { get } } struct OrderedItem : DateSortable { // 隐式一致性,因为sortDate名称与协议匹配 let sortDate:Date } struct Invoice : DateSortable { let invoiceDate:Date // 显式一致性 - 注意必须指定协议名称 // 此外,不能直接访问'invoice.sortDate' // 必须先转换为'DateSortable'类型 var DateSortable.sortDate:Date { return invoiceDate } } let someDate = orderedItem.sortDate // 允许 let someOtherDate = invoice.sortDate // *不允许* let anotherDate = (invoice as! DateSortable).sortDate // 允许
再次说明,以上是伪代码,用来展示其他语言如何支持该特性。
显式一致性的优势
显式一致性主要有两个核心优势:
避免污染自身接口:无需在自身类型中暴露协议要求的成员,就能遵循协议。比如,如果有十个协议都定义了不同名称的日期属性,而你的模型里只有一个对应的日期字段,显式一致性可以让你遵循所有十个协议,却不用在模型的公共接口里添加这十个不同的属性。就像上面的示例里,
Invoice只对外暴露invoiceDate,但依然遵循了要求sortDate的DateSortable协议。解决成员命名冲突:如果有两个不相关的协议(比如来自第三方框架的
Orderable和Deliverable),恰好都定义了同名的var date:Date{ get }属性,而你的PurchaseItem模型里同时有orderDate和deliveryDate两个字段,显式一致性就能解决这个冲突:
extension PurchaseItem : Orderable { var Orderable.date:Date { return orderDate } } extension PurchaseItem : Deliverable { var Deliverable.date:Date { return deliveryDate } }
简单来说,显式接口声明让你可以按功能来关联协议和类型成员,而不是只能靠名称匹配。你依然可以选择隐式匹配(这是Swift默认的方式),但不再是唯一的选择。
Swift是否支持这种特性?
遗憾的是,Swift目前并不支持这种显式的协议一致性语法。
近似方案(但不完全符合需求)
我能想到最接近的实现方式,是在特定作用域内通过私有扩展来添加协议一致性,示例如下:
private extension Invoice : DateSortable { var sortDate:Date { return invoiceDate } } // 注意:invoice.sortDate仅在该扩展所在的作用域内可访问
不过这种方法有不少局限性,比如只有在定义这个私有扩展的文件内部,才能将Invoice实例当作DateSortable来使用,外部文件无法访问这个一致性实现。
内容的提问来源于stack exchange,提问作者Mark A. Donohoe

