Swift中使用any协议类型调用create方法报错,求解决方案
问题:泛型Repository模式中调用
create方法编译报错 我需要实现适配业务场景的Repository模式,代码中尝试调用RepositoryStorageType实现类的create方法时,出现编译错误:
Member 'create' cannot be used on value of type 'any RepositoryStorageType'; consider using a generic constraint instead
相关代码如下:
import Foundation struct ReportingRepository { func addSomething() { let userDefaultsBuilder = UserDefaultsBuilder(key: "isDatasecured", value: true) RepositoryMetadataLayer(storage: .UserDefaultsRepository).create(builder: userDefaultsBuilder) } } struct RepositoryMetadataLayer<BuilderType: RepositoryBuilderType> { let repositoryStorage: any RepositoryStorageType init(storage: RepositoryStorages) { repositoryStorage = storage.repositoryStorage } func create(builder: BuilderType) { // 报错位置 _ = repositoryStorage.create(with: builder) } } enum RepositoryStorages { case CouchbaseRepository case UserDefaultsRepository case KeyChainRepository case FileStorageRepository var repositoryStorage: any RepositoryStorageType { switch self { case .CouchbaseRepository: return CouchbaseRepositoryStorage() case .UserDefaultsRepository: return UserDefaultsRepositoryStorage() case .KeyChainRepository, .FileStorageRepository: return UserDefaultsRepositoryStorage() } } } protocol RepositoryBuilderType { } struct CouchbaseRepositoryBuilder: RepositoryBuilderType { let documentName: String let data: Any } struct UserDefaultsBuilder: RepositoryBuilderType { let key: String let value: Any } protocol RepositoryStorageType { associatedtype BuilderType: RepositoryBuilderType func create(with builder: BuilderType) -> Result<Bool, RepositoryStorageError.Save> } enum RepositoryStorageError: Error { enum Save: Error { case CouchbaseInitialisationFailed case UserDefaultsInitialisatonFailed } enum Fetch: Error { case CouchbaseInitialisationFailed } enum Delete: Error { case CouchbaseInitialisationFailed } } struct UserDefaultsRepositoryStorage: RepositoryStorageType { typealias BuilderType = UserDefaultsBuilder func create(with builder: UserDefaultsBuilder) -> Result<Bool, RepositoryStorageError.Save> { return .failure(.UserDefaultsInitialisatonFailed) } } struct CouchbaseRepositoryStorage: RepositoryStorageType { typealias BuilderType = CouchbaseRepositoryBuilder func create(with builder: CouchbaseRepositoryBuilder) -> Result<Bool, RepositoryStorageError.Save> { .failure(.UserDefaultsInitialisatonFailed) } }
错误原因
RepositoryStorageType包含关联类型BuilderType,any RepositoryStorageType是存在类型——编译器无法确定该类型对应的BuilderType是否与传入的builder参数类型匹配,因此不允许直接调用依赖关联类型的create方法。
解决方案
方案一:通过泛型约束绑定仓库与Builder的类型关系
修改RepositoryMetadataLayer,让它的泛型约束直接绑定具体的RepositoryStorageType实现类,而非仅约束Builder类型,这样编译器能明确两者的类型匹配性:
- 修改
RepositoryMetadataLayer结构:
struct RepositoryMetadataLayer<Storage: RepositoryStorageType> { let repositoryStorage: Storage init(storage: Storage) { repositoryStorage = storage } func create(builder: Storage.BuilderType) { _ = repositoryStorage.create(with: builder) } }
- 调整调用逻辑(直接使用具体仓库实例):
struct ReportingRepository { func addSomething() { let userDefaultsBuilder = UserDefaultsBuilder(key: "isDatasecured", value: true) // 直接实例化具体仓库类型 let storage = UserDefaultsRepositoryStorage() RepositoryMetadataLayer(storage: storage).create(builder: userDefaultsBuilder) } }
如果想保留RepositoryStorages枚举的使用,可给枚举添加泛型工厂方法来返回具体类型:
enum RepositoryStorages { case couchbase case userDefaults case keychain case fileStorage func makeStorage<T: RepositoryStorageType>() -> T? { switch self { case .couchbase: return CouchbaseRepositoryStorage() as? T case .userDefaults, .keychain, .fileStorage: return UserDefaultsRepositoryStorage() as? T } } } // 调用示例 struct ReportingRepository { func addSomething() { let userDefaultsBuilder = UserDefaultsBuilder(key: "isDatasecured", value: true) guard let storage = RepositoryStorages.userDefaults.makeStorage() as UserDefaultsRepositoryStorage? else { // 处理仓库类型转换失败的情况 return } RepositoryMetadataLayer(storage: storage).create(builder: userDefaultsBuilder) } }
方案二:类型擦除(不推荐,丢失编译时类型安全)
如果不想调整泛型结构,可给协议增加类型擦除的通用方法,但此方法会失去编译时类型检查,仅适合临时场景:
- 修改
RepositoryStorageType协议:
protocol RepositoryStorageType { associatedtype BuilderType: RepositoryBuilderType func create(with builder: BuilderType) -> Result<Bool, RepositoryStorageError.Save> // 新增类型擦除方法 func createAny(with builder: any RepositoryBuilderType) -> Result<Bool, RepositoryStorageError.Save>? } // 给协议扩展默认实现 extension RepositoryStorageType { func createAny(with builder: any RepositoryBuilderType) -> Result<Bool, RepositoryStorageError.Save>? { guard let typedBuilder = builder as? BuilderType else { // 类型不匹配时返回nil或自定义错误 return nil } return create(with: typedBuilder) } }
- 修改
RepositoryMetadataLayer的create方法:
struct RepositoryMetadataLayer<BuilderType: RepositoryBuilderType> { let repositoryStorage: any RepositoryStorageType init(storage: RepositoryStorages) { repositoryStorage = storage.repositoryStorage } func create(builder: BuilderType) { _ = repositoryStorage.createAny(with: builder) } }
总结
优先选择方案一,通过泛型约束明确仓库与Builder的类型绑定关系,确保编译时类型安全,符合Repository模式的设计初衷。
内容的提问来源于stack exchange,提问作者user121095
相关产品推荐
相关产品推荐

