Swift 泛型和协议
Swift Generics and Protocols
鉴于以下情况,这将引发有关协议无法遵守自身的编译时错误,只有 struct/enum 可以遵守协议。这似乎违背了能够在泛型中使用协议的目的。我试图理解为什么这不起作用,但如果我删除通用并将协议放在 'Z' 的位置,一切都很好。这似乎与应该允许的协议和泛型背道而驰。
** 为清楚起见而编辑:我需要获取一种可以转换为 [String:MyProtocol]
字典的 Any 类型,并将其传递给方法 printEm
。 printEm
必须使用泛型,因为它将实例化 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: ¤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)
}
}
}
这是一个完美的例子,说明了为什么协议不符合自己。在您的代码中 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?
检查是在该闭包内完成的,其中类型都是已知的。
鉴于以下情况,这将引发有关协议无法遵守自身的编译时错误,只有 struct/enum 可以遵守协议。这似乎违背了能够在泛型中使用协议的目的。我试图理解为什么这不起作用,但如果我删除通用并将协议放在 'Z' 的位置,一切都很好。这似乎与应该允许的协议和泛型背道而驰。
** 为清楚起见而编辑:我需要获取一种可以转换为 [String:MyProtocol]
字典的 Any 类型,并将其传递给方法 printEm
。 printEm
必须使用泛型,因为它将实例化 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: ¤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)
}
}
}
这是一个完美的例子,说明了为什么协议不符合自己。在您的代码中 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?
检查是在该闭包内完成的,其中类型都是已知的。