Swift 泛型和协议

Swift Generics and Protocols

鉴于以下情况,这将引发有关协议无法遵守自身的编译时错误,只有 struct/enum 可以遵守协议。这似乎违背了能够在泛型中使用协议的目的。我试图理解为什么这不起作用,但如果我删除通用并将协议放在 'Z' 的位置,一切都很好。这似乎与应该允许的协议和泛型背道而驰。

** 为清楚起见而编辑:我需要获取一种可以转换为 [String:MyProtocol] 字典的 Any 类型,并将其传递给方法 printEmprintEm 必须使用泛型,因为它将实例化 class 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 并以某种方式对其进行类型检查的能力 and/or 转换它以便它可以在泛型方法中使用。

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: &currentValueArray!, 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: &currentObject!, updates: updates[key]!)
            }
            else {
                var newSyncable = Z()
                try! self.mergeUpdates(source: &newSyncable, updates: updates[key]!)
                source.append(newSyncable)
            }
        }
    }

这是一个完美的例子,说明了为什么协议不符合自己。在您的代码中 ZMyProtocol,因此 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? 检查是在该闭包内完成的,其中类型都是已知的。