如何在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")
Int
是Any
的子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())