如何存储类型Class< ClassImplementingProtocol>的值在Swift中的类型为[String:Class< Protocol>]的字典中? [英] How do I store a value of type Class<ClassImplementingProtocol> in a Dictionary of type [String:Class<Protocol>] in Swift?

查看:98
本文介绍了如何存储类型Class< ClassImplementingProtocol>的值在Swift中的类型为[String:Class< Protocol>]的字典中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在一个类型为[String:SomeClass]的Dictionary中存储一个更专用的类型。以下是一些说明我的问题的示例代码(也可以在 https:// swiftlang中使用。 ng
$ b

  class Thing< T> {} 

协议Flavor {}

class Vanilla:Flavor {}

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

dict [foo] = Thing< Vanilla>()

在第9行,第28行产生错误 ERROR:无法指定'Thing< Vanilla>'类型的值来键入'Thing< Any>?'



我尝试将 Thing< Vanilla>()作为Thing< Flavor> >,但会产生错误无法将'Thing< Vanilla>'类型的值转换为强制中的'Thing< Flavor>'。我也试过将字典定义为 [String:Thing< Any>] 类型,但这也不会改变任何内容。



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



我还应该提到类 Thing 不是由我定义的(实际上它是abo ut BoltsSwift Task s),所以创建不带类型参数的基类 Thing 的解决方案不起作用。 $ C>东西<香精GT; 。 Thing 不是协变的。 Swift没有办法表示 Thing 是协变的。这有很好的理由。如果你所要求的是没有周密的规则,我可以写下面的代码:

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

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

Int 任何的子类型,所以如果 [Int] [Any] ,我可以使用此函数将字符串附加到int数组中。这打破了类型系统。不要这样做。



根据您的确切情况,有两种解决方案。如果它是一个值类型,然后重新打包它:

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

如果是一个引用类型,用类型橡皮擦装箱。例如:

  // struct,除非你必须使这个类适合系统,
//但它可能会有点复杂
struct AnyThing {
let _value:() - > Flavor
var value:Flavor {return _value()}
init< T:Flavor>(thing:Thing< T>){
_value = {return thing.value}
}

$ b $ var dict = [String:AnyThing]()
dict [foo] = AnyThing(thing:Thing< Vanilla>(value:Vanilla()) )

类型橡皮擦的细节可能因您的基础类型而异。






顺便说一句:这方面的诊断非常好。如果您尝试在Xcode 9中调用上面的 addElement ,您会得到:

 不能将不可变值作为inout参数传递:从'[Int]'到'[Any]'的隐式转换需要临时

这是告诉你的是,Swift愿意通过 [Int] 来请求 [Any] code>作为数组的特殊情况(尽管这种特殊的处理不会扩展到其他泛型)。但是它只会通过创建一个临时(不可变的)数组副本来允许它。 (这是另一个例子,很难推断Swift的性能,在其他语言中使用cast的情况下,Swift可能会创建一个副本,或者它可能不会,很难确定)。


I want to store a more specialized type in a Dictionary of type [String:SomeClass]. Here is some sample code illustrating my problem (also available to play with at https://swiftlang.ng.bluemix.net/#/repl/579756cf9966ba6275fc794a):

class Thing<T> {}

protocol Flavor {}

class Vanilla: Flavor {}

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

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

It produces the error ERROR at line 9, col 28: cannot assign value of type 'Thing<Vanilla>' to type 'Thing<Any>?'.

I've tried casting Thing<Vanilla>() as Thing<Flavor> but that produces the error cannot convert value of type 'Thing<Vanilla>' to type 'Thing<Flavor>' in coercion.

I've also tried to define the Dictionary as type [String:Thing<Any>] but that doesn't change anything either.

How do I create a collection of different Things without resorting to plain [String:AnyObject]?

I should also mention that the class Thing is not defined by me (in fact it's about BoltsSwift Tasks), so the solution to create a base class of Thing without a type parameter doesn't work.

解决方案

A Thing<Vanilla> is not a Thing<Flavor>. Thing is not covariant. There is no way in Swift to express that Thing is covariant. There are good reasons for this. If what you were asking for were allowed without careful rules around it, I would be allowed to write the following code:

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

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

Int is a subtype of Any, so if [Int] were a subtype of [Any], I could use this function to append strings to an int array. That breaks the type system. Don't do that.

Depending on your exact situation, there are two solutions. If it is a value type, then repackage it:

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

If it is a reference type, box it with a type eraser. For example:

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

The specifics of the type eraser may be different depending on your underlying type.


BTW: The diagnostics around this have gotten pretty good. If you try to call my addElement above in Xcode 9, you get this:

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

What this is telling you is that Swift is willing to pass [Int] where you ask for [Any] as a special-case for Arrays (though this special treatment isn't extended to other generic types). But it will only allow it by making a temporary (immutable) copy of the array. (This is another example where it can be hard to reason about Swift performance. In situations that look like "casting" in other languages, Swift might make a copy. Or it might not. It's hard to be certain.)

这篇关于如何存储类型Class&lt; ClassImplementingProtocol&gt;的值在Swift中的类型为[String:Class&lt; Protocol&gt;]的字典中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆