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
的包装器泛型类。它并不真正实现它的方法,而是传递给它包装的对象(它是real
GenericRepository

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
的包装器泛型类。它并不真正实现它的方法,而是传递给它包装的对象(它是real
GenericRepository

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,谢谢,可能会在那里注册并遵循该主题。