Swift泛型-当试图使用专用子协议作为变量时,试图使泛型协议具体化失败
我想知道为什么我的Swift泛型-当试图使用专用子协议作为变量时,试图使泛型协议具体化失败,swift,generics,dependency-injection,swift-protocols,Swift,Generics,Dependency Injection,Swift Protocols,我想知道为什么我的SomeResourceRepository仍然是泛型的,尽管它只在一种情况下定义,也就是当我设置ResourceType=SomeResource时,它的XCode格式如下,带有where子句。下面的代码显示了我试图实现的确切设置,写在操场上 我试图为任何给定的ResourceType定义一个通用协议,这样ResourceTypeRepository协议就自动需要相同的函数集,而不必复制粘贴大部分GenericRepository,只需手动填写我创建的每个存储库的Resour
SomeResourceRepository
仍然是泛型的,尽管它只在一种情况下定义,也就是当我设置ResourceType=SomeResource
时,它的XCode格式如下,带有where子句。下面的代码显示了我试图实现的确切设置,写在操场上
我试图为任何给定的ResourceType
定义一个通用协议,这样ResourceTypeRepository
协议就自动需要相同的函数集,而不必复制粘贴大部分GenericRepository
,只需手动填写我创建的每个存储库的ResourceType。我之所以需要它作为协议,是因为我希望能够在以后的测试中模拟它。因此,我将在实际应用程序的其他地方提供上述协议的实现
我对下面代码的解释是,它应该可以工作,因为SomeResourceLocalRepository
和SomeResourceRemoteRepository
都是具体的,因为我通过在“SomeResourceRepository
之上”定义它们,消除了关联的类型,它仅在ResourceType==SomeResource
中定义
import Foundation
struct SomeResource: Identifiable {
let id: String
let name: String
}
struct WhateverResource: Identifiable {
let id: UUID
let count: UInt
}
protocol GenericRepository: class where ResourceType: Identifiable {
associatedtype ResourceType
func index() -> Array<ResourceType>
func show(id: ResourceType.ID) -> ResourceType?
func update(resource: ResourceType)
func delete(id: ResourceType.ID)
}
protocol SomeResourceRepository: GenericRepository where ResourceType == SomeResource {}
protocol SomeResourceLocalRepository: SomeResourceRepository {}
protocol SomeResourceRemoteRepository: SomeResourceRepository {}
class SomeResourceLocalRepositoryImplementation: SomeResourceLocalRepository {
func index() -> Array<SomeResource> {
return []
}
func show(id: String) -> SomeResource? {
return nil
}
func update(resource: SomeResource) {
}
func delete(id: String) {
}
}
class SomeResourceService {
let local: SomeResourceLocalRepository
init(local: SomeResourceLocalRepository) {
self.local = local
}
}
// Some Dip code somewhere
// container.register(.singleton) { SomeResourceLocalRepositoryImplementation() as SomeResourceLocalRepository }
我可能必须找到另一种方法来实现这一点,但这很乏味,也很烦人,因为我们将产生大量重复的代码,当我们决定更改存储库的API时,我们将不得不手动更改所有协议的API,因为在这项工作中我们没有遵循通用的“父”协议
我已经阅读并在这个问题的答案中找到了相关问题,以及其他
我觉得这应该行得通,但是不行。最终目标是一个可用于依赖项注入的具体协议,例如,
container.register(.singleton){ProtocolImplementation()as protocol}
,但当协议的接口显然可以成为通用的时,不需要复制粘贴,如上图所示。as swift提供了一种声明通用协议的方法(使用associatedtype
关键字)在没有其他泛型约束的情况下,不可能声明泛型协议属性。因此,最简单的方法是将资源服务类声明为泛型-class SomeResourceService
import Foundation
struct SomeResource: Identifiable {
let id: String
let name: String
}
struct WhateverResource: Identifiable {
let id: UUID
let count: UInt
}
protocol GenericRepository: class where ResourceType: Identifiable {
associatedtype ResourceType
func index() -> Array<ResourceType>
func show(id: ResourceType.ID) -> ResourceType?
func update(resource: ResourceType)
func delete(id: ResourceType.ID)
}
protocol SomeResourceRepository: GenericRepository where ResourceType == SomeResource {}
protocol SomeResourceLocalRepository: SomeResourceRepository {}
protocol SomeResourceRemoteRepository: SomeResourceRepository {}
class SomeResourceLocalRepositoryImplementation: SomeResourceLocalRepository {
func index() -> Array<SomeResource> {
return []
}
func show(id: String) -> SomeResource? {
return nil
}
func update(resource: SomeResource) {
}
func delete(id: String) {
}
}
class SomeResourceService {
let local: SomeResourceLocalRepository
init(local: SomeResourceLocalRepository) {
self.local = local
}
}
// Some Dip code somewhere
// container.register(.singleton) { SomeResourceLocalRepositoryImplementation() as SomeResourceLocalRepository }
但是这个解决方案有一个很大的缺点——您需要在这个服务涉及的任何地方约束泛型
通过将local
声明为具体的泛型类型,可以从服务声明中删除泛型约束。但是如何从泛型协议转换到具体的泛型类呢
有一种方法。你可以定义一个符合GenericRepository
的包装器泛型类。它并不真正实现它的方法,而是传递给它包装的对象(它是realGenericRepository
)
class AnyGenericRepository<ResourceType: Identifiable>: GenericRepository {
// any usage of GenericRepository must be a generic argument
init<Base: GenericRepository>(_ base: Base) where Base.ResourceType == ResourceType {
// we cannot store Base as a class property without putting it in generics list
// but we can store closures instead
indexGetter = { base.index() }
// and same for other methods or properties
// if GenericRepository contained a generic method it would be impossible to make
}
private let indexGetter: () -> [ResourceType] {
indexGetter()
}
// ... other GenericRepository methods
}
由于swift提供了一种声明泛型协议的方法(使用
associatedtype
关键字),因此在没有其他泛型约束的情况下,不可能声明泛型协议属性。因此最简单的方法是声明资源服务类泛型-类SomeResourceService
import Foundation
struct SomeResource: Identifiable {
let id: String
let name: String
}
struct WhateverResource: Identifiable {
let id: UUID
let count: UInt
}
protocol GenericRepository: class where ResourceType: Identifiable {
associatedtype ResourceType
func index() -> Array<ResourceType>
func show(id: ResourceType.ID) -> ResourceType?
func update(resource: ResourceType)
func delete(id: ResourceType.ID)
}
protocol SomeResourceRepository: GenericRepository where ResourceType == SomeResource {}
protocol SomeResourceLocalRepository: SomeResourceRepository {}
protocol SomeResourceRemoteRepository: SomeResourceRepository {}
class SomeResourceLocalRepositoryImplementation: SomeResourceLocalRepository {
func index() -> Array<SomeResource> {
return []
}
func show(id: String) -> SomeResource? {
return nil
}
func update(resource: SomeResource) {
}
func delete(id: String) {
}
}
class SomeResourceService {
let local: SomeResourceLocalRepository
init(local: SomeResourceLocalRepository) {
self.local = local
}
}
// Some Dip code somewhere
// container.register(.singleton) { SomeResourceLocalRepositoryImplementation() as SomeResourceLocalRepository }
但是这个解决方案有一个很大的缺点——您需要在这个服务涉及的任何地方约束泛型
通过将local
声明为具体的泛型类型,可以从服务声明中删除泛型约束。但是如何从泛型协议转换到具体的泛型类呢
有一种方法。你可以定义一个符合GenericRepository
的包装器泛型类。它并不真正实现它的方法,而是传递给它包装的对象(它是realGenericRepository
)
class AnyGenericRepository<ResourceType: Identifiable>: GenericRepository {
// any usage of GenericRepository must be a generic argument
init<Base: GenericRepository>(_ base: Base) where Base.ResourceType == ResourceType {
// we cannot store Base as a class property without putting it in generics list
// but we can store closures instead
indexGetter = { base.index() }
// and same for other methods or properties
// if GenericRepository contained a generic method it would be impossible to make
}
private let indexGetter: () -> [ResourceType] {
indexGetter()
}
// ... other GenericRepository methods
}
我在Swift论坛上看到过这篇文章,这似乎是一个类似的问题。还没有答案。谢谢。@rraphael,可能会在那里注册并遵循该主题。我在Swift论坛上看到过这篇文章,这似乎是一个类似的问题。还没有答案。@rraphael,谢谢,可能会在那里注册并遵循该主题。