当我Collection
延长时count
的类型为IndexDistance
。
当我延长Array
时,count
类型为Int
为什么会有这样的区别?这是最近的变化还是总是这样?
我已经读过这个answer,但无法获得太多。
我认为唯一相关但却不了解的是:
另一个优点是这个[IndexDistance]也能正常工作 使用数组切片(第一个元素的索引不是 必须为零
不确定这意味着什么。
我问的原因是,为什么代码会在Collection上抛出错误但在Array上没有这样做...即使count
最终都是{{1} }}
Int
修改
根据Martin和其他人的评论,我添加了一个额外的问题。可能这是我提问的根本原因......
是否意味着在extension Collection where Element: Comparable{
func whatever(){
for index in 0...count{ // binary operator '...' cannot be applied to operands of type 'Int' and 'Self.IndexDistance'
}
}
}
extension Array where Element: Comparable{
func whatever(){
for index in 0...count{ // NO ERROR
}
}
}
类型中,Collection
不是定义为IndexDistance
。基本上一般在“协议”中#39; level associatedTypes 未定义 ...它正在等待具体的类型来执行此操作?是对的吗?
话虽如此,在“协议”中访问Int
是否有任何有意义的用例。水平?我的意思是你不能将它与任何count
进行比较,所以它似乎没用。
答案 0 :(得分:10)
来自Swift编程语言中的Associated Types(强调添加):
在定义协议时,将一个或多个关联类型声明为协议定义的一部分有时很有用。关联类型为占位符名称提供用作协议一部分的类型。 在采用协议之前,不会指定用于该关联类型的实际类型。使用associatedtype关键字指定关联类型。
在 Swift 3 / 4.0,中,Collection
协议定义了五种相关类型
(来自What’s in a Collection?):
protocol Collection: Indexable, Sequence {
associatedtype Iterator: IteratorProtocol = IndexingIterator<Self>
associatedtype SubSequence: IndexableBase, Sequence = Slice<Self>
associatedtype Index: Comparable // declared in IndexableBase
associatedtype IndexDistance: SignedInteger = Int
associatedtype Indices: IndexableBase, Sequence = DefaultIndices<Self>
...
}
这里
associatedtype IndexDistance: SignedInteger = Int
是具有类型约束(: SignedInteger
)和默认值(= Int
)的关联类型声明,
如果类型T
采用协议但未定义T.IndexDistance
,则T.IndexDistance
将成为Int
的类型别名。
许多标准集合类型都是这种情况
(例如Array
或String
),但不适用于所有人。例如
public struct AnyCollection<Element> : Collection
来自Swift标准库的定义
public typealias IndexDistance = IntMax
您可以使用
进行验证let ac = AnyCollection([1, 2, 3])
let cnt = ac.count
print(type(of: cnt)) // Int64
如果您愿意,也可以使用非Int
索引距离定义自己的集合类型:
struct MyCollection : Collection {
typealias IndexDistance = Int16
var startIndex: Int { return 0 }
var endIndex: Int { return 3 }
subscript(position: Int) -> String {
return "\(position)"
}
func index(after i: Int) -> Int {
return i + 1
}
}
因此,如果您扩展具体类型Array
,那么count
是Int
:
extension Array {
func whatever() {
let cnt = count // type is `Int`
}
}
但是在协议扩展方法中
extension Collection {
func whatever() {
let cnt = count // some `SignedInteger`
}
}
您知道的一切是cnt
的类型是某些类型采用的
SignedInteger
协议,但不一定是Int
。人们还可以
当然,与伯爵一起工作。
for index in 0...count { // binary operator '...' cannot be applied to operands of type 'Int' and 'Self.IndexDistance'
具有误导性。整数文字0
可以推断为a
来自上下文的Collection.IndexDistance
(因为SignedInteger
符合ExpressibleByIntegerLiteral
)。但是SignedInteger
范围不是Sequence
,这就是它无法编译的原因。
所以这会起作用,例如:
extension Collection {
func whatever() {
for i in stride(from: 0, to: count, by: 1) {
// ...
}
}
}
从 Swift 4.1开始, IndexDistance
不再使用了
收集索引之间的距离现在始终表示为Int
,请参阅
特别是count
的返回类型是Int
。有一个类型别名
typealias IndexDistance = Int
使旧的代码编译,但是已被弃用并且将被删除 在未来版本的Swift中。
答案 1 :(得分:1)
不完全是答案,但作为OP,我认为这些都是我理解的重要先决条件。我不知道:
associatedtype
associatedtype
提供默认类型associatedtype
可以完成对协议的typealias
的遵守。 associatedtype
的一致性可以通过其他方式完成,即默认。associatedType
的默认类型未在协议级别触发&#39;即它只受限于其约束类型。然而,一旦类/结构采用它...然后只使用默认类型。有关详情,请参阅上面的Martin's answer和associatedtype associatedtype
。请参阅最后提供的链接。基本上,您可以通过隐式定义associatedtype
SomeClass9
// associatedtype isn't constrained
protocol NotConstrained{
associatedtype IndexDistance
}
// associatedtype is constrained
protocol Constrained{
associatedtype IndexDistance: SignedInteger
}
// associatedtype is constrained and defaulted
protocol ConstrainedAndDefaulted{
associatedtype IndexDistance: SignedInteger = Int
}
// All Good
class someClass1: NotConstrained{
typealias IndexDistance = Int
}
// All Good
class someClass2: NotConstrained{
typealias IndexDistance = String // It works with String as well, since it wasn't constrained
}
// Not Good
class SomeClass3: NotConstrained{
// error: type 'SomeClass3' does not conform to protocol 'NotConstrained'
// doesn't work because we MUST have a typealias
}
// All Good
class SomeClass4: Constrained{
typealias IndexDistance = Int16
}
// Not Good
class SomeClass5: Constrained{
typealias IndexDistance = String
// error: type 'SomeClass5' does not conform to protocol 'Constrained'
// Obviously! Because String isn't of type 'SignedIngeter'
}
// Not Good
class SomeClass6: Constrained{
// error: type 'SomeClass6' does not conform to protocol 'Constrained'
}
// All Good
class SomeClass7: ConstrainedAndDefaulted{
// NO ERROR, because the associatedtype has already defaulted
}
// All Good
class SomeClass8: ConstrainedAndDefaulted{
typealias IndexDistance = Int64 // We changed the default from 'Int' to 'Int64'
// Which is ok because 'Int64' is of type 'SignedInteger'
}
class SomeClass9<T> : NotConstrained {
typealias IndexDistance = T
}
如果您能理解为什么class SomeClass8
无误地运作,那么您就得到了答案!
可以在here找到一个非常简单的读物。我非常喜欢这篇文章如何定义隐式和显式与协议相关类型之间的区别
Understanding protocol associated types and their constraints教程非常精彩。
我将不得不回到这里并使用上面的教程更新我的答案。但在此之前请参考链接。这真的很有帮助。