一尘不染

如何存储Class类型的值 在[String:Class类型的字典中]在Swift中?

swift

我想在[String:SomeClass]类型的Dictionary中存储一个更专业的类型。这是一些说明我的问题的示例代码(也可以在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>?'

我已经尝试过强制转换,Thing<Vanilla>() as Thing<Flavor>但是会产生错误cannot convert value of type 'Thing<Vanilla>' to type 'Thing<Flavor>' in coercion

我也尝试将Dictionary定义为type,[String:Thing<Any>]但这也没有任何改变。

如何在Thing不求助于平原的情况下创建不同s 的集合[String:AnyObject]

我还应该提到该类Thing不是我定义的(实际上是关于BoltsSwift Task的),因此创建Thing没有类型参数的基类的解决方案不起作用。


阅读 226

收藏
2020-07-07

共1个答案

一尘不染

A
Thing<Vanilla>不是Thing<Flavor>Thing不是协变的。Swift中无法表达Thing协变。这有充分的理由。如果没有严格的规则就允许您要求的内容,则可以编写以下代码:

func addElement(array: inout [Any], object: Any) {
    array.append(object)
}

var intArray: [Int] = [1]
addElement(array: &intArray, object: "Stuff")

Int是的子类型Any,因此如果[Int]是的子类型[Any],则可以使用此函数将字符串追加到int数组。这打破了类型系统。不要那样做

根据您的实际情况,有两种解决方案。如果是值类型,则将其重新打包:

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

如果是引用类型,请在其旁边加上类型橡皮擦。例如:

// 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()))

类型擦除器的具体信息可能会有所不同,具体取决于您的基础类型。


顺便说一句:关于此问题的诊断已经相当不错。如果您尝试addElement在Xcode 9中调用上述代码,则会得到以下信息:

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

这告诉您的是,Swift愿意将[Int]您要求的位置[Any]作为数组的特殊情况传递给您(尽管这种特殊待遇并未扩展到其他泛型类型)。 但是
它只能通过制作数组的临时(不可变)副本来允许它。(这是另一个很难解释Swift性能的例子。在其他语言中看起来像“广播”的情况下,Swift可能会复制一个副本。或者可能不会。但是很难确定。)

2020-07-07