Swift 为自定义类型添加 Comparable 一致性

162 阅读2分钟

Swift擅长使用协议和协议扩展将功能扩展到很多地方。 例如,我们知道这4 < 5是真的, Swift 将该功能扩展到整数数组:我们可以比较数组中的所有整数来决定每个整数应该位于其他整数之前还是之后。然后 Swift 使用该结果对数组进行排序。

let values = [1, 5, 3, 6, 2, 9].sorted()  

我们不需要告诉sorted()应该如何工作,因为它理解整数数组如何工作 。

如果对一个自定义的结构体User,需要对一个User数组进行排序,那么sorted()就不起作用了。

struct User: Identifiable {

    let id = UUID()
    var firstName: String
    var lastName: String
}  

当然,我们可以这样:

let users = [

    User(firstName: "Arnold", lastName: "Rimmer"),
    User(firstName: "Kristine", lastName: "Kochanski"),
    User(firstName: "David", lastName: "Lister"),

].sorted {
    $0.lastName < $1.lastName
}  

它不是一个理想的解决方案。

Swift有更好的解决方案。整数数组有一个没有参数的简单方法sorted(),因为 Swift 知道如何比较两个整数。用编码术语来说,Int符合Comparable协议,这意味着它定义了一个函数,该函数接受两个整数,如果第一个整数应该排在第二个整数之前,则返回 true。

我们可以使我们自己的类型符合Comparable协议,当我们这样做时,我们也得到一个没有参数的方法sorted()。

这需要两个步骤:

  1. 将Comparable一致性添加到 User的定义中。
  2. 添加一个名为 < 的方法,该方法需要两个用户,如果第一个用户应该在第二个用户之前排序,则返回 true。
struct User: Identifiable, Comparable {

    let id = UUID()
    var firstName: String
    var lastName: String

    static func <(lhs: User, rhs: User) -> Bool {

        lhs.lastName < rhs.lastName
    }
}  

首先,是的,该方法只是被调用<,即“小于”运算符。该方法的工作是确定一个用户是否“小于”(在排序意义上)另一个用户,因此我们正在向现有运算符添加功能。这称为运算符重载。

其次,lhs和rhs是“左侧”和“右侧”的编码约定的缩写,使用它们是因为<运算符的左侧有一个操作数,右侧有一个操作数。

第三,这个方法必须返回一个布尔值,这意味着我们必须决定一个对象是否应该排在另一个对象之前。

第四,该方法必须标记为static,这意味着直接在结构上调用它,User而不是在结构的单个实例上调用。

最后,我们这里的逻辑非常简单:我们只是传递与我们的属性之一的比较,要求 Swift 用于<两个姓氏字符串。您可以添加任意数量的逻辑,根据需要比较任意数量的属性,但最终您需要返回 true 或 false。

现在,这意味着这种代码现在可以工作:

let users = [

    User(firstName: "Arnold", lastName: "Rimmer"),
    User(firstName: "Kristine", lastName: "Kochanski"),
    User(firstName: "David", lastName: "Lister"),

].sorted()