【问题标题】:Swift Generics and ProtocolsSwift 泛型和协议
【发布时间】:2021-03-01 19:40:01
【问题描述】:

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

**为清楚问题而编辑:我需要采用可以转换为 [String:MyProtocol] 字典的 Any 类型并将其传递给方法 printEmprintEm 必须使用泛型,因为它将实例化 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: &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)
            }
        }
    }

【问题讨论】:

  • (1) 你想做什么? (2) 问题是什么?
  • @matt 我需要上面的这两种方法不会引发编译错误。更具体地说,我需要能够获取具有通用协议的字典并将其传递给通用方法。所以,我需要能够将 [String:MyProtocol] 传递给 func printEm(theThigns; [String:Z]) 以便我可以做 theThings["key"].whoAmI 以及 Z() .除此之外,我想了解为什么当前的实现不起作用,因为它似乎是泛型和协议应该能够在许多其他语言中处理和执行的本质。
  • 正如错误所说,协议不符合自己,但您不需要它们。你的通用没有做任何事情。摆脱它。这应该是func printEm(theThings: [String: MyProtocol])
  • "将 [String:MyProtocol] 传递给 func printEm(theThigns; [String:Z])" - 不,这不是 Z 的意思。 Z:MyProtocol 表示“MyProtocol 采用者”。您将通过 [String:What] 符合 MyProtocol 的内容。
  • 是的,我知道我能做到,但那行不通。我将在上面进行调整以说明为什么这在我的特定情况下不起作用。有没有办法获得用于uknownThings 的泛型并将其作为Z 传递?

标签: swift generics protocols


【解决方案1】:

这是一个完美的例子,说明为什么协议不符合自己。在您的代码中,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) -&gt; () 闭包擦除(隐藏)。运行时as? 检查是在闭包内完成的,类型都是已知的。

【讨论】:

  • 伙计,我对这个非常兴奋,但不幸的是它与我的用例不匹配。我可能从我的例子中煮沸了太多的复杂性。我已经继续并添加了我更复杂的示例,说明为什么上述内容无法解决我的问题。我没有“事物”的实例,所以我需要调用 Z(),因为我没有当前实例来拉出该输入。
  • 另外,很好地解释了为什么错误是这样的。我现在明白了很多。我开始怀疑是否可以从 Any 中提取类型,然后在调用该方法时使用它来阐明 Z 是什么。
  • 泛型在编译时被完全评估。我认为您正在尝试在运行时评估它们,但是到那时为时已晚。所有函数都生成了。
  • 例如,数组在元素之上是通用的。所以你需要在编译时了解 Element。没有 Array&lt;type(of: object)&gt; 这样的东西(在运行时评估)。编译器不知道它有多大。
  • 那么,那个解决方案就被否决了。考虑到 Swift 已经编译,我觉得这有点牵强。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-09
  • 1970-01-01
相关资源
最近更新 更多