Swift中条件一致性的解决方法(是:将协议添加到受约束的泛型类型)

时间:2016-09-24 02:20:10

标签: swift

下面的代码片段演示了我要解决的问题。

import Foundation

protocol Printable {
    func className() -> String
}

class SomeType: Printable {
    func className() -> String {
        return "SomeType"
    }
}

class List<T> {
}

extension List where T: SomeType {
    func className() -> String {
        return "List<SomeType>"
    }
}

func test(type: Any, message: String) {
    guard type is Printable else {
        print("\(message): ERROR")
        return
    }
    print("\(message): SUCCESS")
}

let s: Any = SomeType()
test(type: s, message: "#1")

let slist1: Any = List<Any>()
test(type: slist1, message: "#2")

let slist2: Any = List<SomeType>()
test(type: slist2, message: "#3")

我怎样才能得到这个:

> #1: SUCCESS  <--- as expected
> #2: ERROR    <--- it's okay
> #3: SUCCESS  <--- I am getting ERROR instead

似乎在此行中添加协议可以解决问题:

extension List: Printable where T: SomeType { // COMPILE ERROR

但不幸的是,这是不允许的。

另一种方法是使用:

extension List where T: Printable { // COMPILES OK in Swift 2.3 but doesn't work. COMPILE ERROR in Swift 3.0

但是,再次,没有运气通过测试。

我还可以将协议添加到受约束的泛型类型中吗?

2 个答案:

答案 0 :(得分:1)

好吧,在你的警卫中,你问“这是否是可打印的,然后打印成功,否则打印错误”和你的第一个例子,你有一个可打印的SomeType。没关系。

之后你有slist1,类型为List<Any>,它绝对不是可打印的类型,你会得到“错误”。那没关系

接下来你有List<SomeType>。现在你有一个类扩展,将T定义为SomeType,对吗?但是你只是将T定义为SomeType而不是实际的 List ,所以当你将整个List传递给测试函数时,你不会让你的测试通过,因为List<AnyTypeHere>不可打印,因为列表本身不实现Printable。

现在问题是,您想要整个列表是否可打印?如果是这样,那么只需使其符合SomeType或Printable协议即可。除了将单个List<SomeType>元素传递给函数之外,这是唯一可以通过的方法。你的功能逻辑是正确的,但这只是对概念的误用。

因此,如果您希望List<SomeType>进行该传递,那么您可以执行类似

的操作
class List<T> : Printable where T:SomeType {
  //Add code here that conforms to protocol
}

这样做会使你的第二次测试失败,因为Any不会从SomeType继承,但它会让你的第三次测试通过,因为现在List<T>是Printable而T也是SomeType类型。我的意思是,这只是一个真正快速的方式来获得你想要开始的东西。除非你添加额外的东西,否则你不会同时传递第二个和第三个测试,因为第二个测试是List类型为Any,而第三个测试是List类型为Printable。因此,其中任何一个都会抛出错误(因为List不是Printable类型)或者所有测试都显示成功(因为List类型为Printable)

答案 1 :(得分:1)

现在有了Swift 4.2,这是一个更新的答案,现在将条件一致性添加为功能。

在他们的spec on Github中,这种代码现已有效

   extension List : Printable where T: SomeType {
       func className() -> String {
           return "List<SomeType>"
       }
   }