在泛型处理链中,我有一个处理结果的方法,如果泛型类型碰巧符合协议,我想以不同的方式处理。
我想重载基于协议一致性的方法。这种切换方法可以通过编译器(如果基于协议一致性的切换则不能)。
不幸的是,当处理链中的一个方法还没有为该协议类型化时,编译器从不使用更具体的重载。
下面是一个例子:
protocol MyProtocol {
var protocolString: String { get }
}
struct SpecialResult: MyProtocol {
let protocolString: String = "special MyProtocol"
}
func preProcess<T>(result: T) {
// Process chain for any kind of T
print("PreProcess: T conforms to MyProtocol: \(T.self is MyProtocol.Type)") // always prints true
process(result: result)
}
func process<T>(result: T) where T: MyProtocol { // Doesn't matter if I use process<T: MyProtocol>
// process specifically for MyProtocol
print("I'm processing a \(result.protocolString) result!")
}
func process<T>(result: T) {
// process generically for T
print("I'm processing a result.")
print("Process: T conforms to MyProtocol: \(T.self is MyProtocol.Type)") // always prints true
}
let result = SpecialResult()
preProcess(result: result)
process(result: result)
// PreProcess: T conforms to MyProtocol: true
// I'm processing a result.
// Process: T conforms to MyProtocol: true
// I'm processing a special MyProtocol result!
我希望它打印特殊的MyProtocol行两次。
有什么解决方案吗?即使没有方法重载,我似乎也不能让路由工作。
我可以跳过重载并使用两个不同的方法名,在if T.self is MyProtocol.Type
后面调用每个方法,但编译器仍然抱怨true case需要符合MyProtocol,如果它是true case,它显然必须符合!
我可以强制转换result as? MyProtocol
,但这样我就失去了它的底层类型,这是我在整个处理链中所需要的。我真的需要一种方法来做类似result as? T: MyProtocol
的事情,但我似乎找不到一种方法来...
3条答案
按热度按时间qnakjoqk1#
你在这里有一些错误。最基本的是这个结构:
那是不对的。你的意思是:
或者你可以用途:
(but您可能应该使用
result
版本)你使用的东西大概是“T是MyProtocol类型的子类型”,这意味着“T是一个元类型”,这真的不是你的意思(事实上,我不确定它有什么有用的意思)。
所以这给你:
由于你想在运行时考虑类型,你需要使用运行时检查(即“if”语句,而不是泛型)。我不是100%清楚你想从中得到什么,但我希望它沿着这些路线:
这就产生了:
当然,你也可以用一个紧凑的
??
来重写这个大的if
语句:或者,您可以将处理过程拆分到
if
中,而不是使用字符串值或其他方式。你也可以这样写(这可能是你的意思):
我倾向于对重载使用相同的名称有点小心,像这样,它可能会有歧义(我可能会命名第一个
process(myProtocol:)
)。它完全可以工作,并且非常有用(请参阅stdlib中Encoder容器中的各种encode
方法)。但它也可能有点混乱,所以应该非常小心地使用它。但最重要的是matt的回答。泛型不是动态分派。它们是在编译时基于编译时信息100%确定的。一般来说,泛型重载应该只用于提高性能(支持BidirectionalCollections比Collections更好的算法)。它们不应该用于改变行为。这条路很少会以你期望的方式工作。
hxzsmxv22#
你似乎认为这里会发生某种动态分派,但事实并非如此,泛型不是这样的。
泛型是在编译时解析的。运行时不会根据参数的实时检查来覆盖编译器的行为。在
preProcess
中,编译器只知道这个东西是一个T,它可以是任何类型。所以它选择通用实现。如果你想在代码中检查这是什么类型的东西,并向下转换以告诉编译器,很好,你可以这样做。但是运行时不会为你做这件事。
xdnvmnnf3#
当你写:
编译器知道
result
的类型,但在func preProcess<T>(result:)
中,T
可以是任何的东西,因此它将始终调用非专用版本process<T>(result:)
。帮助编译器保留类型信息的一种方法是添加
Processor
协议:这个想法是让符合类型来处理结果,这里它只是描述,但它可以是任何东西。
现在有两个选择
使用方法:
或者为每个已知类型的结果指定一个专用类:
使用方法: