如何在Swift中的types为的Dictionary中存储Class <ClassImplementingProtocol>types的值?

我想在一个types为[String:SomeClass]的字典中存储一个更专门化的types。 下面是一些示例代码,说明我的问题(也可以在https://swiftlang.ng.bluemix.net/#/repl/579756cf9966ba6275fc794a上玩):

class Thing<T> {} protocol Flavor {} class Vanilla: Flavor {} var dict = [String:Thing<Flavor>]() dict["foo"] = Thing<Vanilla>() 

它会ERROR at line 9, col 28: cannot assign value of type 'Thing<Vanilla>' to type 'Thing<Any>?'产生错误ERROR at line 9, col 28: cannot assign value of type 'Thing<Vanilla>' to type 'Thing<Any>?'

我尝试过将Thing<Vanilla>() as Thing<Flavor>但是产生这个错误cannot convert value of type 'Thing<Vanilla>' to type 'Thing<Flavor>' in coercion

我也试过把字典定义为types[String:Thing<Any>]但是也不会改变任何东西。

如何创build一个不同的Thing的集合而不诉诸普通的[String:AnyObject]

我还应该提到Thing类不是由我定义的(实际上它是关于BoltsSwift Task ),所以没有types参数创buildThing基类的解决scheme不起作用。

Thing<Vanilla>不是一Thing<Flavor>Thing不是协变的。 Swift没有办法expressionThing是协变的。 这有很好的理由。 如果你所要求的是没有经过周密的规定,我可以写下面的代码:

 func addElement(array: inout [Any], object: Any) { array.append(object) } var intArray: [Int] = [1] addElement(array: &intArray, object: "Stuff") 

IntAny的子types,所以如果[Int][Any]的子types,我可以使用这个函数将string附加到int数组中。 这打破了types系统。 不要这样做。

根据你的具体情况,有两种解决scheme。 如果它是一个值types,然后重新打包它:

 let thing = Thing<Vanilla>(value: Vanilla()) dict["foo"] = Thing(value: thing.value) 

如果是参考types,请使用橡皮擦来装箱。 例如:

 // struct unless you have to make this a class to fit into the system, // but then it may be a bit more complicated struct AnyThing { let _value: () -> Flavor var value: Flavor { return _value() } init<T: Flavor>(thing: Thing<T>) { _value = { return thing.value } } } var dict = [String:AnyThing]() dict["foo"] = AnyThing(thing: Thing<Vanilla>(value: Vanilla())) 

橡皮擦的细节可能会根据您的基础types而有所不同。


顺便说一句:关于这方面的诊断已经相当不错了。 如果你尝试在Xcode 9中调用上面的addElement ,你会得到这个:

 Cannot pass immutable value as inout argument: implicit conversion from '[Int]' to '[Any]' requires a temporary 

这是告诉你的是,Swift愿意把你所要求的[Int]作为数组的特例(尽pipe这个特殊的处理没有扩展到其他generics)。 它只会通过创build一个临时(不可变)的数组副本来实现。 (这是另外一个很难说明Swift性能的例子,在其他语言中看起来像“cast”的情况下,Swift可能会做一个副本,或者它不可能,很难确定)。

解决这个问题的一个办法是给Thing添加一个初始化器,并创build一个可以容纳一个Vanilla物体的Thing<Flavor>

它看起来像这样:

 class Thing<T> { init(thing : T) { } } protocol Flavor {} class Vanilla: Flavor {} var dict = [String:Thing<Flavor>]() dict["foo"] = Thing<Flavor>(thing: Vanilla())