类型约束中的多个协议

alt*_*ser 7 generics swift

我想使用泛型类和类型约束:

class MyCustomClass<T : Equatable> {
  var a: Array<T>
  init() {
    a = Array<T>()
  }
}
Run Code Online (Sandbox Code Playgroud)

这很好.但是,如果我想使用第二个协议,会发生什么

class MyCustomClass<T : Equatable, IndexableBase> {
  var a: Array<T>
  init() {
    a = Array<T>()
  }
}
Run Code Online (Sandbox Code Playgroud)

它说初始化程序失败了,因为我必须使用2而不是1个参数.我不明白.

dfr*_*fri 17

斯威夫特3

(我在你的例子中替换IndexableBaseCollection你应该更喜欢后者)


根据Swift 3,协议组合使用中缀运算符&而不是之前的protocol<...>构造,如接受和实现的进化提议中所述:

因此,使用协议组合,您可以将组合类型约束放在T参数列表中的占位符中:

class MyCustomClass<T: Equatable & Collection> { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

作为协议组合的替代方法,您还可以使用where子句来连接多个类型(和子类型)约束.根据已接受和实施的进化建议:

where子句已移至声明的末尾,在这种情况下,您的示例将如下所示:

class MyCustomClass<T: Equatable> where T: Comparable { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

或者,甚至将完整的协议组成与where声明末尾的条款放在一起

class MyCustomClass<T> where T: Equatable & Comparable { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

我们应该选择哪种风格?

有趣的讨论是我们应该考虑的"最佳实践",因为这个特定主题没有指定Swift API指南.我们放置

  • 参数列表中的所有(主要类型)约束,或
  • 声明结尾处的所有约束,或
  • 将参数列表中的一些约束分开,将其他约束放在声明的末尾?

考虑下面的设计示例,其中我们有一个构造协议,我们计划将其用作类型约束(Doable),它本身包含一个associatedtype我们可以放置类型约束的类型.

protocol Doable { 
    associatedtype U
}
Run Code Online (Sandbox Code Playgroud)

使用上面的不同方法,以下三个替代方法在语法上都是有效的

// alternative #1
func foo<T: Equatable & Doable>(_ bar: T) -> () where T.U: Comparable { /* ... */ }

// alternative #2
func foo<T: Equatable>(_ bar: T) -> () where T: Doable, T.U: Comparable { /* ... */ }

// alternative #3
func foo<T>(_ bar: T) -> () where T: Equatable & Doable, T.U: Comparable { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

我将引用苹果开发人员Joe Groff从SE-0081提出的演变主题:

这是一个判断电话.我觉得在很多情况下,一个通用参数至少受到一个值得提前调用的重要协议或基类的限制,因此允许这样的事情是合理的.

func foo<C: Collection>(x: C) -> C.Element 
Run Code Online (Sandbox Code Playgroud)

没有将Collection约束放在距离声明前面太远的地方.

因此,在上面的设计示例中,可能适合将协议组合用于直接应用于通用占位符的类型约束,T并将这些约束放在参数列表中,而子类型约束T.U放在声明的末尾.即,上面的替代#1.


Fra*_*oni 2

你可以使用这个解决方法

class MyCustomClass<T: Equatable where T: IndexableBase > {
    var a: Array<T>
    init() {
        a = Array<T>()
    }
}
Run Code Online (Sandbox Code Playgroud)

斯威夫特4:

class MyCustomClass<T: Equatable> where T: Collection  {
    var a: Array<T>
    init() {
        a = Array<T>()
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @altralaser即使这在 Swift 3 中对你“有效”,它也应该给你一个警告,即[**强调**我的]:_“警告:泛型参数旁边的'`where`'子句**已被弃用并且将在未来版本的 Swift 中删除**"_. 因此,您可能希望使用与 Swift 3 保持同步的解决方案,否则您的实现将在 Swift 的未来更新中被破坏(请参阅 Swift 3 最新版本的其他两个答案)。 (4认同)