swift - Swift 泛型方法不使用大多数特定类型
问题描述
我有一个适用于几种类型的标记协议
protocol Foobar {}
extension String: Foobar {}
extension Int: Foobar {}
我想为这个协议定义一个实现一些默认行为的通用方法,但我可以为特定类型重载。这有效:
func foobar<T: Foobar>(_ t: T) {
print("default")
}
func foobar(_ t: Int) {
print("int")
}
foobar("hi.") // default
foobar(3) // int
问题是如果我将它包装在另一个通用方法调用中
func wrapper<U>(_ t: U) where U: Foobar {
foobar(t)
}
wrapper("hi.") // default
wrapper(3) // default <---- !!!
我曾希望编译器看到Int: Foobar
sowrapper(3)
是用U == Int
(即最具体的类型)编译的,因此行为与foobar(3)
. 然而似乎相反U == Foobar
。
有没有不同的方法来写这个,这样wrapper(3)
做同样的事情foobar(3)
?我知道我可以在其中执行运行时类型检查以强制执行相同的行为,但在我看来,编译器应该能够弄清楚。
正如评论中指出的那样,我可以专攻wrapper
:
func wrapper(_ t: Int) { foobar(t) }
这行得通,但想象一下我有一堆其他类型也符合Foobar
并有自己的重载。现在我必须写
func wrapper(_ t: Int) { foobar(t) }
func wrapper(_ t: Double) { foobar(t) }
func wrapper(_ t: String) { foobar(t) }
func wrapper(_ t: Bool) { foobar(t) }
// ...
使用这样的代码,我希望我可以使用泛型来避免重复
解决方案
这些评论让我确信,我想要的解决方案是不可能的,所以我想出了一种不同的方法,可以在不重复代码的情况下实现类似的功能。
首先要注意的是,各种foobar
方法在 Swift 中确实没有关系。因为它们的参数类型不同,所以尽管名称相同,但它们被视为完全不同的方法。所以还不如不要试图让它们看起来一样
func foobarDefault<T: Foobar>(_ t: T) {
print("default")
}
func foobarInt(_ t: Int) {
print("int")
}
现在问题变得更清楚了:你有完全不同的方法,怎么wrapper
知道要调用哪一个作为它的参数?简单:将信息放入协议中!
protocol Foobar {
static var foobar: (Self) -> Void { get }
}
// default implementation
extension Foobar {
static var foobar: (Self) -> Void { return foobarDefault }
}
// conformance
extension String: Foobar {}
extension Int: Foobar { static let foobar = foobarInt }
包装器实现很简单
func wrapper<U: Foobar>(_ t: U) {
U.foobar(t)
}
这正是我想要的,唯一的区别是你在协议一致性旁边指定了 foobar 重载,现在我想到它就更好了。就我而言,我什至决定省略默认扩展名,以便更明确地说明每种类型如何实现协议!
正如杰西指出的那样,这是一个非常愚蠢的例子。在我的实际应用中,foobar
方法更复杂。这更像
struct Validator {
// has some internal data determining how it validates things
let blah = ...
// knows how to validate everything based on its own data
func validateDefault<T: Foobar>(_ t: T) -> Bool { ... }
func validateInt(_ t: Int) -> Bool { ... }
}
因为验证器是在运行时动态构建的,所以包装器需要注入一个验证器wrapper(foobar, validator)
。那么这里怎么设置呢?我选择这样做
protocol Foobar {
static var validate: (Validator) -> (Self) -> Bool
}
extension Int: Foobar {
static let validate = Validator.validateInt // so simple!
}
func wrapper<U: Foobar>(_ t: U, _ validator: Validator) -> Bool {
return U.foobar(validator)(t)
}
是的,你可以用实例方法来代替,但这有点糟糕:
protocol Foobar {
func validate(_ validator: Validator) -> Bool
}
extension Int: Foobar { // this is the part that gets repeated for every type!
func validate(_ validator: Validator) -> Bool {
return validator.validateInt(self)
}
} // much noisier
func wrapper<U: Foobar>(_ t: U, _ validator: Validator) -> Bool {
return t.foobar(validator)
}
推荐阅读
- python - 有没有办法将字符串拆分成块并存储在python列表中
- java - WeakReference、reachabilityFence 和 Java 内存模型
- vue.js - 显示表数据以 vuetify 选择标签
- javascript - javascript getImageData() 方法不起作用
- dataframe - 使用 r 在 csv 中的数据表中添加一行
- dart - 我在飞镖中不明白的三件事 - 你能解释一下吗?
- typescript - 如何使用泛型定义 Typescript 类型
- python - 通过命令行将参数传递给 asyncio.run() 函数
- node.js - 在 NodeJS 中调用外部 API 时出现以下错误
- python - after() 方法暂时冻结 tkinter GUI