我认为这里的类型关系相当明显,但我错过了错误的原因。错误是:"键入' T'不符合协议' EntityType'" (在ThingManager
):
//
protocol EntityType {
typealias Identifier
var identifier : Identifier { get }
}
class EntityWithStringIdentifier : EntityType {
var identifier : String
init (i:String) { self.identifier = i }
}
class Thing : EntityWithStringIdentifier {}
//
protocol EntityManager {
typealias Entity : EntityType
func has (entity:Entity) -> Bool
}
class BaseEntityManager<Entity:EntityType> : EntityManager {
func has (entity:Entity) -> Bool { return true }
}
// Type 'T' does not conform to protocol 'EntityType'
class ThingManager<T:Thing> : BaseEntityManager<T> {
}
T
是Thing
的子类型; Thing
是实现EntityWithStringIdentifier
的{{1}}的子类型。 那么,为什么错误 ?
显然,使用以下内容可以避免错误:
EntityType
然后可以使用class ThingManager<T:Thing where T:EntityType> : BaseEntityManager<T> {
}
实例化ThingManager
(首先暗示Thing
已实施Thing
...)
EntityType
从类型的角度来看,有没有更好的方法来实现这种DAO模式?
答案 0 :(得分:2)
随着Swift 2.0的发布,以早期版本的Swift来回答这个问题似乎毫无意义,很快就会过时。我可以确认这个问题 - 我认为是一个bug - 仍然存在于Swift 2.0中。但是,我可以建议一种稍微不同的方式来组织代码,以缓解问题,同时仍然有一个干净的实现:跳过ThingManager
类并改为使用协议扩展。
因此,将所有代码保留为BaseEntityManager
,您必须:
// NOTE "where Entity: Thing". This is the key.
extension EntityManager where Entity: Thing {
func useAThing(thing: Thing) {
}
}
let tm = BaseEntityManager<Thing>()
tm.useAThing(Thing(i: "thing"))
我怀疑这最终将成为做事的“快捷方式”。 Swift的泛型对我来说简直很奇怪。他们经常违反最低惊喜原则,恕我直言。但如果你了解他们的怪癖,最终他们会非常强大。这种方法的唯一缺点是访问修饰符和封装。 useAThing
方法无权访问EntityManagerBase
的私有状态,根据您的具体情况,这可能是也可能不是问题。
最后,如果采用这种方法,我建议重命名。 BaseEntityManager
如果你从不继承它,那就像是错误的名字。相反,我会将其称为EntityManager
并将协议重命名为EntityManagerType
以遵循Apple的惯例。