关于如何在Swift中执行类型擦除,已经有很多问题,我看到类型擦除常常被描述为处理具有关联类型和泛型类型的协议的一种重要模式。
然而,在我看来,需要类型擦除通常是设计问题的症状--您本质上是在“丢弃”类型信息(即将值放入容器或将其传递给函数),这些信息最终通常需要通过冗长而易碎的向下转换来恢复。也许我不明白的是像AnyHashable这样的“类型”的用例--带有self的PATs/协议只能用作泛型约束,因为它们没有具体化类型,这让我想知道有什么令人信服的理由想要将它们具体化。
简而言之,在Swift中使用类型擦除是的好想法吗?我正在寻找一些关于何时使用此模式的一般指导方针,以及一些实际用例的示例,其中类型擦除优于其替代方法。
发布于 2021-04-30 19:24:21
我试图找到一个极简的类型擦除的例子。根据我的经验,它往往变得更加复杂,我尽量避免它。但有时候就是这样。
它终于和以前一样复杂了,有了古老的语言。除了旧式的语言是通过崩溃伤害你,而快速伤害你在建筑时间。
它是一种强类型语言,因此它不适合泛型语言。
假设您需要管理文档中的一些形状。这些形状是Identifiables,这意味着它们有一个由关联类型决定的id。在这种情况下。
下面的代码不会生成,因为它不能直接使用Shape协议,因为id类型是由符合形状协议的对象定义的关联类型
import Foundation
protocol Shape: AnyShape, Identifiable {
var name: String { get }
}
struct Square: Shape {
var id: Int = 0
var name: String { "Square" }
}
func selectShape(_ shape: Shape) {
print("\(shape.name) selected")
}通过添加类型擦除的形状,然后可以将其传递给函数。因此,这将建立:
import Foundation
protocol AnyShape {
var name: String { get }
}
protocol Shape: AnyShape, Identifiable {
}
struct Square: Shape {
var id: Int = 0
var name: String { "Square" }
}
func selectShape(_ shape: AnyShape) {
print("\(shape.name) selected")
}简单用例.
假设现在我们的应用程序连接到两个形状的制造商服务器,以获取它们的目录并与我们的服务器同步。
我们知道世界各地的形状都是形状,但是数据库中的ACME形状工厂索引是Int,而Shapers俱乐部使用UUID。
在这一点上,我们需要‘恢复’的类型,正如你说的。这正是在查看AnyHashable源代码文档时所解释的。
Cast是无法避免的,它最终对应用程序的安全性和模型的可靠性都是一件好事。
第一部分是协议,它可能是冗长的,随着情况的增加而变得复杂,但它将是应用程序的通信基础框架,不应该经常改变。
import Foundation
// MARK: - Protocols
protocol AnyShape {
var baseID: Any { get }
var name: String { get }
}
// Common functions to all shapes
extension AnyShape {
func sameObject(as shape: AnyShape) -> Bool {
switch shape.baseID.self {
case is Int:
guard let l = baseID as? UUID , let r = shape.baseID as? UUID else { return false }
return l == r
case is UUID:
guard let l = baseID as? UUID , let r = shape.baseID as? UUID else { return false }
return l == r
default:
return false
}
}
func sameShape(as shape: AnyShape) -> Bool {
return name == shape.name
}
func selectShape(_ shape: AnyShape) {
print("\(shape.name) selected")
}
}
protocol Shape: AnyShape, Identifiable {
}
extension Shape {
var baseID: Any { id }
}第二部分是模型-这将有望随着我们与更多的形状制造商的工作而发展。
可以对形状执行的敏感操作不在此代码中。因此,创建和调整模型和apis没有问题。
// MARK: - Models
struct ACME_ShapeFactory_Model {
struct Square: Shape {
var id: Int = 0
var name: String { "Square" }
var ACME_Special_Feature: Bool
}
}
struct ShapersClub_Model {
struct Square: Shape {
var id: UUID = UUID()
var name: String { "Square" }
var promoCode: String
}
}测试
let shape1: AnyShape = ACME_ShapeFactory_Model.Square()
let shape2: AnyShape = ShapersClub_Model.Square()
let shape3: AnyShape = ShapersClub_Model.Square()
Compare two different shapes references from different manufacturers
shape1.sameObject(as: shape2) : false
-> Logic, it can't be the same item if it comes from different manufacturers
Compare two different shapes references from same manufacturers
shape2.sameObject(as: shape3) : false
-> This is a new shape, sync in catalog
Compare two identical shapes references from same manufacturers
shape2.sameObject(as: shape2) : true
-> We already have this one in the catalog
Compare two shapes from different manufacturers
shape1.sameShape(as: shape2) : true
-> Dear customer, we have two kind of squares from two manufacturers就这样。我希望这会有什么帮助。欢迎任何更正或评论。
最后,我对我的形状制造商的名字感到非常自豪:)
https://stackoverflow.com/questions/67337673
复制相似问题