是否有办法将扩展应用于泛型类型,以使其符合协议,当该扩展仅对泛型类型的某些特化有效时?
例如,考虑这个协议,它返回一个字典,计算符合协议的实例所包含的值的频率:
// a type conforming to this protocol should return a dictionary
// which counts the frequencies of values contained by the type instance
protocol ConvertibleToFrequencyDictionary {
typealias ItemType
func dictionaryCountingFrequencies<ItemType:Hashable>() -> Dictionary<ItemType,Int>
}
由于要计数的值必须作为字典的键,因此值必须是符合Hashable的类型。这由通用dictionaryCountingFrequencies
方法定义的类型约束表示。 (我没有看到任何方法直接在关联类型上定义类型约束,例如,在“typealias”声明中。)
现在考虑在Array上使用此扩展,以使其符合协议:
extension Array : ConvertibleToFrequencyDictionary {
typealias ItemType=Element
func dictionaryCountingFrequencies<ItemType:Hashable>() -> Dictionary<ItemType,Int> {
var valueToCount = Dictionary<ItemType,Int>()
for item in self {
if let existingCount = valueToCount[item] {
valueToCount.updateValue(value: existingCount + 1, forKey: item)
} else {
valueToCount.updateValue(value: 1, forKey: item)
}
}
return valueToCount;
}
}
这应该返回Array中出现的每个不同值的频率。但是,当然,由于这些值必须是可清除的,因此此扩展名仅在应用于Array<T:Hashable>
时才有效。
然而,这对Array<Int>
不起作用,即使Int是Hashable。
为什么不呢?如果您在泛型类型上编写扩展,那么该扩展是否必须能够适用于泛型类型的每个可能的特化?
答案 0 :(得分:0)
您可以展开SequenceType
而不是Array
,这会为您提供更多适用的类型,然后使用Element
限制where
的类型。
Array
个扩展名需要where Element : SomeProtocol
SequenceType
个扩展名需要where Generator.Element : SomeProtocol
您遇到的问题不是Int
不是Hashable
,而是项目不一定是Hashable
。对ItemType
的强制转换也有效,因为在此示例中始终为Hashable
。
就个人而言,我不会使用通用的。由于Element已经涵盖了您可以存储在Array中的所有内容,因此无需将其设置为类型模糊。
extension SequenceType where Generator.Element : Hashable {
func dictionaryCountingFrequencies() -> Dictionary<Generator.Element,Int> {
var valueToCount = Dictionary<Generator.Element,Int>()
for item in self {
if let existingCount = valueToCount[item] {
valueToCount.updateValue(existingCount + 1, forKey: item)
} else {
valueToCount.updateValue(1, forKey: item)
}
}
return valueToCount;
}
}
let array = [1,2,3,4,5,1,1,1,2,3,3]
let freqDict = array.dictionaryCountingFrequencies()
// prints [5: 1, 2: 2, 3: 3, 1: 4, 4: 1]
freqDict[5]
// prints 1