如何使用Swift字典存储泛型对象[T:C<;T>;]?

如何使用Swift字典存储泛型对象[T:C<;T>;]?,swift,dictionary,generics,subscript,Swift,Dictionary,Generics,Subscript,我很难使用Swift字典来存储通用对象 我尝试以下列方式存储对象: [C: Container<C>], where C: ContentType 我有许多不同的“ContentType”子类,每个子类描述一种特定类型的内容 class ContentTypeA: ContentType { ... } class ContentTypeB: ContentType { ... } 容器 我还有一个名为“Container”的类,用于存储与内容类型相关的信息。恰当地说,它是一个泛

我很难使用Swift字典来存储通用对象

我尝试以下列方式存储对象:

[C: Container<C>], where C: ContentType
我有许多不同的“ContentType”子类,每个子类描述一种特定类型的内容

class ContentTypeA: ContentType { ... }
class ContentTypeB: ContentType { ... }
容器 我还有一个名为“Container”的类,用于存储与内容类型相关的信息。恰当地说,它是一个泛型类,其泛型类型为“ContentType”

控制器 在我的主控制器类中,我实现了以下下标重载

subscript<C: ContentType>(contentType: C) -> Container<C> {
    // If Container for ContentType "C" does not exist
    if (self.containers[contentType] == nil) {
        // Then create it
        self.containers[contentType] = (Container<C>.init() as! Container<ContentType>)
    }
    // And return it
    return self.containers[contentType]! as! Container<C>
}
从这里,可以调用特定于每个ContentType的容器扩展

class Container<C: ContentType>: NSObject { }
extension Container where C: ContentTypeA {
    func fancyFunctionOnlyFoundInContentTypeA() { }
}

extension Container where C: ContentTypeB {
    func fancyFunctionOnlyFoundInContentTypeB() { }
}
containerForTypeA.fancyFunctionOnlyFoundInContentTypeA()
containerForTypeB.fancyFunctionOnlyFoundInContentTypeB()
问题 我相信所有这些在理论上都是可行的,而且它确实可以在Swift 4、Xcode 9.0中编译。但是,在执行以下操作时:

// Then create it
self.containers[contentType] = (Container<C>.init() as! Container<ContentType>)
//然后创建它
self.containers[contentType]=(Container.init()作为!Container)
我得到以下错误:

Could not cast value of type 'Test.Container<Test.ContentTypeA>' (0x109824a00) to 'Test.Container<Test.ContentType>' (0x109824638).
无法将“Test.Container”(0x109824a00)类型的值强制转换为“Test.Container”(0x109824638)。
由于ContentTypeA继承自ContentType,我不理解为什么这是一个问题。Swift字典似乎无法存储ContentType类型的对象,必须将对象存储为ContentType

有人有什么建议吗?干杯

完整代码如下:
类ViewController:UIViewController{
var containers=[ContentType:Container]()
重写func viewDidLoad(){
super.viewDidLoad()
设contentTypeA=contentTypeA.init()
让contentTypeB=contentTypeB.init()
让containerForTypeA=self[contentTypeA]
让containerForTypeB=self[contentTypeB]
containerForTypeA.fancyFunctionOnlyFoundInContentTypeA()
containerForTypeB.fancyFunctionOnlyFoundInContentTypeB()中
}
下标(contentType:C)->容器{
if(self.containers[contentType]==nil){
self.containers[contentType]=(Container.init()作为!Container)
}
返回self.containers[contentType]!as!Container
}
}
类ContentType:NSObject{}
类容器:NSObject{}
类ContentTypeA:ContentType{}
类ContentTypeB:ContentType{}
扩展容器,其中C:ContentTypeA{
func fancyFunctionOnlyFoundInContentTypeA(){}
}
扩展容器,其中C:ContentTypeB{
func fancyFunctionOnlyFoundInContentTypeB(){}
}

发生崩溃是因为Swift泛型是不变的,因此
容器
不是
容器
的子类,即使
B
将是
a
的子类,任何向下转换都将失败,强制转换将导致崩溃

您可以通过在字典中使用
AnyObject
作为基类来解决这个问题,因为
Container
是从
NSObject
派生的:

var containers = [ContentType : AnyObject]()

subscript<C: ContentType>(contentType: C) -> Container<C> {
    if let container = containers[contentType] {
        return container as! Container<C>
    } else {
        let container = Container<C>()
        containers[contentType] = container
        return container
    }
}
var containers=[ContentType:AnyObject]()
下标(contentType:C)->容器{
如果let container=containers[contentType]{
将容器作为!容器返回
}否则{
let container=container()
容器[contentType]=容器
返回容器
}
}

Swift是否支持逆变泛型(和相关类型转换)?
Test.Container
基本上与
Test.Container无关,因为泛型是不变的。考虑一个<代码>设置>代码> TEX>代码>测试。容器< /代码>设置泛型类型的变量:如果将容器“uppe”到一个更一般的泛型,那么调用该类的实例,那么会发生什么?然后继续使用uncasted对象,变量现在会有什么值?应该这样做;)接下来,如果这段代码变得更复杂,那么创建一个非泛型的
容器
超类也可能值得考虑。谢谢Cristik(很抱歉回复太慢)!您的解决方案工作得非常出色,但我已经放弃了在本例中使用泛型的尝试,因为它们被证明比它们的价值更麻烦。干杯@LukeFletcher是的,有时泛型会增加不必要的复杂性,如果类型安全可以通过更传统的方式实现,那么建议采用这些方式。
// Then create it
self.containers[contentType] = (Container<C>.init() as! Container<ContentType>)
Could not cast value of type 'Test.Container<Test.ContentTypeA>' (0x109824a00) to 'Test.Container<Test.ContentType>' (0x109824638).
class ViewController: UIViewController {

    var containers = [ContentType : Container<ContentType>]()

    override func viewDidLoad() {
        super.viewDidLoad()

        let contentTypeA = ContentTypeA.init()
        let contentTypeB = ContentTypeB.init()

        let containerForTypeA = self[contentTypeA]
        let containerForTypeB = self[contentTypeB]

        containerForTypeA.fancyFunctionOnlyFoundInContentTypeA()
        containerForTypeB.fancyFunctionOnlyFoundInContentTypeB()
    }

    subscript<C: ContentType>(contentType: C) -> Container<C> {
        if (self.containers[contentType] == nil) {
            self.containers[contentType] = (Container<C>.init() as! Container<ContentType>)
        }
        return self.containers[contentType]! as! Container<C>
    }

}

class ContentType: NSObject { }
class Container<C: ContentType>: NSObject { }
class ContentTypeA: ContentType { }
class ContentTypeB: ContentType { }

extension Container where C: ContentTypeA {
    func fancyFunctionOnlyFoundInContentTypeA() { }
}

extension Container where C: ContentTypeB {
    func fancyFunctionOnlyFoundInContentTypeB() { }
}
var containers = [ContentType : AnyObject]()

subscript<C: ContentType>(contentType: C) -> Container<C> {
    if let container = containers[contentType] {
        return container as! Container<C>
    } else {
        let container = Container<C>()
        containers[contentType] = container
        return container
    }
}