Swift:如何为带关联类型的KeyValueStore指定参数及成员变量类型
核心问题
- 如何在
ConsumerA类中正确声明KeyValueStore对应的Key、Value类型? - 从
example方法的实现可以看出我需要使用String和Int作为对应的类型,我该怎么限制参数和成员变量,仅允许接受Key为String、Value为Int且遵守KeyValueStore协议的类实例呢?
补充参考方案
我后续找到了通过类型擦除的方式实现的解决方案,供大家参考:
class AnyKeyValueStore<Key, Value>: KeyValueStore { private let _set: (Value?, Key) -> Void private let _object: (Key) -> Value? init<U: KeyValueStore>(_ keyValueStore: U) where U.Key == Key, U.Value == Value { _set = keyValueStore.set _object = keyValueStore.object } func set(value: Value?, forKey key: Key) { _set(value, key) } func object(forKey key: Key) -> Value? { return _object(key) } }
解决方案
有两种常用方案可以解决带关联类型协议的使用问题,适配不同的业务场景:
方案1:给消费者类添加泛型约束(最简实现)
直接在ConsumerA的定义处添加泛型参数,约束其遵守KeyValueStore协议,且关联类型匹配你需要的String/Int即可:
class ConsumerA<T: KeyValueStore> where T.Key == String, T.Value == Int { var keyValueStore: T init(store: T) { keyValueStore = store } func example() { keyValueStore.set(key: "Hello", value: 5) } }
该方案写法简单,无额外封装成本,编译时类型安全,没有性能损耗。缺点是ConsumerA的类型会和传入的存储实现绑定,无法在运行时动态切换不同的KeyValueStore实现。
方案2:使用类型擦除封装(适配动态切换场景)
如果需要支持运行时动态切换存储实现,就用你补充的AnyKeyValueStore类型擦除封装,隐藏具体的实现类型,ConsumerA直接使用擦除后的类型即可:
class ConsumerA { var keyValueStore: AnyKeyValueStore<String, Int> init(store: AnyKeyValueStore<String, Int>) { keyValueStore = store } func example() { keyValueStore.set(value: 5, forKey: "Hello") } } // 使用示例 // 传入UserDefaults实现 let userDefaultStore = AnyKeyValueStore(UserDefaultsStore()) let consumerA = ConsumerA(store: userDefaultStore) // 运行时切换为内存存储实现 consumerA.keyValueStore = AnyKeyValueStore(MemoryKeyValueStore())
该方案的优势是隐藏了具体的实现类型,支持运行时动态切换不同的存储实现,缺点是需要额外编写一层类型擦除的封装,有极少量的性能开销,绝大多数场景下可以忽略。
内容的提问来源于stack exchange,提问作者Austin E
相关产品推荐
相关产品推荐

