为了账号安全,请及时绑定邮箱和手机立即绑定

如何在Swift的[String:Class <Protocol>]类型的字典中存储Class

如何在Swift的[String:Class <Protocol>]类型的字典中存储Class

炎炎设计 2019-11-04 13:10:32
我想在[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没有类型参数的基类的解决方案不起作用。
查看完整描述

3 回答

?
皈依舞

TA贡献1851条经验 获得超3个赞

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可能会复制一个副本。或者可能不会复制。很难确定。)


查看完整回答
反对 回复 2019-11-04
?
Smart猫小萌

TA贡献1911条经验 获得超7个赞

解决此问题的一种方法是向其中添加一个初始化程序,Thing并创建一个Thing<Flavor>保存Vanilla对象的对象。


它看起来像:


class Thing<T> {


    init(thing : T) {

    }


}


protocol Flavor {}


class Vanilla: Flavor {}


var dict = [String:Thing<Flavor>]()


dict["foo"] = Thing<Flavor>(thing: Vanilla())


查看完整回答
反对 回复 2019-11-04
?
婷婷同学_

TA贡献1844条经验 获得超8个赞

请注意,您的反例inout仅说明了作为inout参数传递的变量必须具有不变性(inout读和写该变量都可以)。并不是因为任意泛型都是不变的(实际上,正如您在编辑中强调的那样,这Array是一种特殊情况- [Int]确实是的子类型[Any],但是您的示例仍然正确地是非法的)。您也可以看到根本没有泛型,例如func foo(a: inout Any, b: Any) { a = b }; var i = 1; foo(a: &i, b: "Stuff"),这是非法的。

查看完整回答
反对 回复 2019-11-04
  • 3 回答
  • 0 关注
  • 733 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信