鉴于以下情况,这将引发关于协议无法遵守自身并且只有结构/枚举可以遵守协议的编译时错误。这似乎违背了能够在泛型中使用协议的目的。我试图理解为什么这不起作用,但是如果我删除泛型并将协议放在“Z”所在的位置,一切都很好。这似乎与应该允许哪些协议和泛型背道而驰。
**为问题的清晰度进行编辑:我需要采用一种可以转换为 [String:MyProtocol]
字典的 Any 类型,并将其传递给方法 printEm
。 printEm
必须使用泛型,因为它将实例化类 Z
的实例。
protocol MyProtocol {
init()
var whoAmI:String { get }
}
func genericPassing(unknownThing:Any) {
let knownThing = unknownThing as? [String:MyProtocol]
if(knownThing != nil){
self.printEm(knownThing)
}
}
func printEm<Z:MyProtocol>(theThings:[String:Z]) {
let zCollection:[Z] = []
for thing in theThings {
print(thing.whoAmI)
zCollection.append(Z())
}
}
**编辑了 printEm 以说明为什么需要泛型。
** 编辑更复杂的代码。两个主要要求是使用泛型来调用 Z() 以及能够接受 Any 并以某种方式对其进行类型检查和/或强制转换,以便它可以在泛型方法中使用。
private func mergeUpdates<Z:RemoteDataSyncable>(source:inout Z, updates:[WritableKeyPath<Z, Any>:Any]) throws {
for key in updates.keys {
let value = updates[key]!
let valueDict = value as? [String:[WritableKeyPath<RemoteDataSyncable, Any>:Any]]
if(valueDict != nil) {
var currentValueArray = source[keyPath: key] as? [RemoteDataSyncable]
if(currentValueArray != nil) {
self.mergeUpdates(source: ¤tValueArray!, updates: valueDict!)
}
else {
throw SyncError.TypeError
}
}
else {
source[keyPath: key] = value
}
}
}
private func mergeUpdates<Z:RemoteDataSyncable>(source:inout [Z], updates:[String:[WritableKeyPath<Z,Any>:Any]]) {
for key in updates.keys {
var currentObject = source.first { syncable -> Bool in
return syncable.identifier == key
}
if(currentObject != nil) {
try! self.mergeUpdates(source: ¤tObject!, updates: updates[key]!)
}
else {
var newSyncable = Z()
try! self.mergeUpdates(source: &newSyncable, updates: updates[key]!)
source.append(newSyncable)
}
}
}
答案 0 :(得分:1)
这是为什么协议不符合自身的完美例子。在您的代码中,Z
是 MyProtocol
,所以 Z()
是 MyProtocol()
。这将如何运作?它是什么类型?它有多大?然后您不能将它们放入 [Z]
中,因为它们可能是不同的类型。
您的意思是传递任意 MyProtocols 并在每个元素的类型上调用 init
:
func printEm(theThings:[String: MyProtocol]) {
var zCollection:[MyProtocol] = []
for thing in theThings.values {
print(thing.whoAmI)
zCollection.append(type(of: thing).init())
}
}
当我建议使用闭包时,这就是我的意思。这个Updater可以接受任意的ReferenceWritableKeyPaths,当你传递给它任何更新值时,它会分配给每一个可以接受它的键路径。这有点没用,但显示了技术。 (请记住,更新程序会保留对象,因此这可能是您需要解决的问题。)
class Updater {
private(set) var updaters: [(Any) -> ()] = []
func add<Root, Value>(keyPath: ReferenceWritableKeyPath<Root, Value>, on root: Root) {
updaters.append { value in
if let value = value as? Value {
root[keyPath: keyPath] = value
}
}
}
func updateAll(with value: Any) {
for updater in updaters {
updater(value)
}
}
}
class Client {
var updateMe: Int = 0
}
let client = Client()
let updater = Updater()
updater.add(keyPath: \.updateMe, on: client)
updater.updateAll(with: 3)
client.updateMe // 3
这段代码中的关键教训是 add
上的泛型类型在编译时被 (Any) -> ()
闭包擦除(隐藏)。并且运行时 as?
检查在该闭包内完成,其中的类型都是已知的。