You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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类型,这样编译器能明确两者的类型匹配性:

  1. 修改RepositoryMetadataLayer结构:
struct RepositoryMetadataLayer<Storage: RepositoryStorageType> {
    let repositoryStorage: Storage
    
    init(storage: Storage) {
        repositoryStorage = storage
    }
    
    func create(builder: Storage.BuilderType) {
        _ = repositoryStorage.create(with: builder)
    }
}
  1. 调整调用逻辑(直接使用具体仓库实例):
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)
    }
}

方案二:类型擦除(不推荐,丢失编译时类型安全)

如果不想调整泛型结构,可给协议增加类型擦除的通用方法,但此方法会失去编译时类型检查,仅适合临时场景:

  1. 修改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)
    }
}
  1. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 10:14:56