Swift: 在参数/成员变量中设置协议的关联类型
Swift: Set protocol's associated type in argument / member variable
我正在尝试创建一个通用协议,它可以在我的应用程序的各个部分重复使用。以前,我已将 KeyValueStore
硬编码为仅将 String
和 Int
分别用于 Key
和 Value
。我 运行 遇到一个问题,我想要一个不同的消费者,class ConsumerB
,使用 KeyValueStore
而 Key
/Value
是 Int
/Int
分别.
protocol KeyValueStore {
associatedtype Key
associatedtype Value
func get(key: Key) -> Value
func set(key: Key, value: Value)
}
对于最初的实现,我有两个 class 符合协议。 UserDefaultsStore
用于在运行时存储数据,MemoryKeyValueStore
用于单元测试。
class UserDefaultsStore: KeyValueStore {
func get(key: String) -> Int { return 0
// Implementation
}
func set(key: String, value: Int) {
// Implementation
}
}
class MemoryKeyValueStore: KeyValueStore {
var values: [String: Int] = [:]
func get(key: String) -> Int { return values[key]! }
func set(key: String, value: Int) { values[key] = value}
}
因为他们都使用 KeyValueStore
硬编码类型,所以我可以在运行时提供存储实现。
class ConsumerA {
var keyValueStore: KeyValueStore
init(store: KeyValueStore) {
keyValueStore = store
}
func example() {
keyValueStore.set(key: "Hello", value: 5)
}
}
添加关联类型后,出现以下错误。这是有道理的,在编译 ConsumerA
时,无法知道 Key
和 Value
在 KeyValueStore
参数和成员变量中采用什么类型。然而,在四处寻找之后,我似乎无法找到关于 如何正确声明 Key
和 Value
应该在 ConsumerA
[=64= 中的类型的明确答案].
在example
函数中,明明会使用String
和Int
,但是如何设置argument/member变量只允许[=64] =]es 符合 KeyValueStore
并且 Key
/Value
设置为 String
/Int
?
Protocol 'KeyValueStore' can only be used as a generic constraint because it has Self or associated type requirements
编辑:
根据晚上晚些时候发现的 this 媒体文章,我最终选择了不同的路线。
虽然看起来 Swift 本身应该提供一种方法来将具有关联类型的协议声明为 argument/variable 类型,但这里是解决方法。
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)
}
}
ConsumerA
需要通用。将 ModuleName
替换为您的实际模块名称。
class ConsumerA<KeyValueStore: ModuleName.KeyValueStore>
where KeyValueStore.Key == String, KeyValueStore.Value == Int {
此外,您的方法对应该是下标。
subscript(key: Key) -> Value { get set }
keyValueStore["Hello"] = 5
我正在尝试创建一个通用协议,它可以在我的应用程序的各个部分重复使用。以前,我已将 KeyValueStore
硬编码为仅将 String
和 Int
分别用于 Key
和 Value
。我 运行 遇到一个问题,我想要一个不同的消费者,class ConsumerB
,使用 KeyValueStore
而 Key
/Value
是 Int
/Int
分别.
protocol KeyValueStore {
associatedtype Key
associatedtype Value
func get(key: Key) -> Value
func set(key: Key, value: Value)
}
对于最初的实现,我有两个 class 符合协议。 UserDefaultsStore
用于在运行时存储数据,MemoryKeyValueStore
用于单元测试。
class UserDefaultsStore: KeyValueStore {
func get(key: String) -> Int { return 0
// Implementation
}
func set(key: String, value: Int) {
// Implementation
}
}
class MemoryKeyValueStore: KeyValueStore {
var values: [String: Int] = [:]
func get(key: String) -> Int { return values[key]! }
func set(key: String, value: Int) { values[key] = value}
}
因为他们都使用 KeyValueStore
硬编码类型,所以我可以在运行时提供存储实现。
class ConsumerA {
var keyValueStore: KeyValueStore
init(store: KeyValueStore) {
keyValueStore = store
}
func example() {
keyValueStore.set(key: "Hello", value: 5)
}
}
添加关联类型后,出现以下错误。这是有道理的,在编译 ConsumerA
时,无法知道 Key
和 Value
在 KeyValueStore
参数和成员变量中采用什么类型。然而,在四处寻找之后,我似乎无法找到关于 如何正确声明 Key
和 Value
应该在 ConsumerA
[=64= 中的类型的明确答案].
在example
函数中,明明会使用String
和Int
,但是如何设置argument/member变量只允许[=64] =]es 符合 KeyValueStore
并且 Key
/Value
设置为 String
/Int
?
Protocol 'KeyValueStore' can only be used as a generic constraint because it has Self or associated type requirements
编辑: 根据晚上晚些时候发现的 this 媒体文章,我最终选择了不同的路线。 虽然看起来 Swift 本身应该提供一种方法来将具有关联类型的协议声明为 argument/variable 类型,但这里是解决方法。
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)
}
}
ConsumerA
需要通用。将 ModuleName
替换为您的实际模块名称。
class ConsumerA<KeyValueStore: ModuleName.KeyValueStore>
where KeyValueStore.Key == String, KeyValueStore.Value == Int {
此外,您的方法对应该是下标。
subscript(key: Key) -> Value { get set }
keyValueStore["Hello"] = 5