Swift 流利的 api 带有自定义运算符的设计
Swift fluent-like api design with custom operators
我想用自定义运算符和函数编写一些类似流利的 api。这是我正在寻找的示例:
var result = [Section]()
result +++= Section()
.appendTitle(title)
.appendPhotos(photos)
result +++= Section()
.appendSomething(sth)
.appendPhotos(photos)
.appendAnything()
return result
为此,我声明了两个自定义运算符:
infix operator +++{ associativity left precedence 95 }
func +++(lhs : [Section], rhs : Section) -> [Section] {
var result = lhs
result.append(rhs)
return result
}
infix operator +++= { associativity left precedence 95 }
func +++=(inout lhs : [Section], rhs : Section) {
lhs = lhs +++ rhs
}
当然还有 Section
结构的正确扩展:
extension Section {
mutating func appendTitle(title: String?) -> Section {
guard let unwrappedTitle = title
else { return self }
...
return self
}
mutating func appendPhotos(photos: [Photo]?) -> Section {
...
}
...
}
不幸的是,它没有按预期工作...
单独的 result +++= Section()
行是正确的,但是当我添加 .append
时它无法编译。
第一条消息是:Passing value of type '[Section]' to an inout parameter requires explicit '&'
然后我试着在result
前面放一个&
(但我从来没有为a += 1
做过)有第二条消息:Cannot use mutating member on immutable value: function call returns immutable value
因此,如果有人可以提供帮助,我们将不胜感激。
我正在使用 Swift 2.2 和 Xcode 7.
找到了!!!
这些方法不应该发生变化:
func appendTitle(title: String?) -> Section {
guard let unwrappedTitle = title
else { return self }
var result = self
... // Work with result instead of self!!!
return result
}
好吧,您找到了解决方案。太棒了!
但是请允许我解释一下这里到底发生了什么。
让我们仔细看看您之前的实施。
extension Section {
mutating func appendTitle(title: String?) -> Section {
guard let unwrappedTitle = title else { return self }
self.title = unwrappedTitle
return self
}
mutating func appendPhotos(photos: [Photo]?) -> Section {
guard let unwrappedPhotos = photos else { return self }
self.photos = unwrappedPhotos
return self
}
}
我们知道(至少现在)运算符重载函数绝对没问题,没有错。
给定的扩展正在改变参数的值,因此它应该是变异的,确实是。那为什么会报错:
"Cannot use mutating member on immutable value: function call returns
immutable value"
原因:当调用"appendTitle"或"appendPhotos"时,它return是self的一个副本。不完全相同的引用!
这是因为 swift 处理不变性的方式。如果某个值类型的成员 属性 的值发生更改,则会创建一个副本,并将该值分配给相应的成员。
因此,当你写:Section().appendTitle(title).appendPhotos(photos)
Section()
returns 是 self 的副本,即 Section 的不可变版本。
因此,人们不能 appendTitle
它,或者一般不能改变它。
可以解决:
var section = Section()
section.appendTitle(title).appendPhotos(photos)
但现在 appendTitle(title)
return 是 self 的副本,即 Section 的不可变版本。同样的问题仍然存在。
您所做的是,您没有使用 self
更改任何内容,而是创建了一个新实例,这次是使用 var
的可变实例。对其进行变异并 return 对其进行编辑。现在它不会改变自己,因此它不必是 mutating func
.
您提供的解决方案在这里工作得很好,但实际上它是一个 hack,这里需要它来涵盖我现在在 Swift 中所说的 "bug"。
其他解决方案可以将 struct Section
更改为 class Section
。在那里您不需要创建 var result
,只需更改属性和 return.
这是我在 SO 上的第一个回答:)
我想用自定义运算符和函数编写一些类似流利的 api。这是我正在寻找的示例:
var result = [Section]()
result +++= Section()
.appendTitle(title)
.appendPhotos(photos)
result +++= Section()
.appendSomething(sth)
.appendPhotos(photos)
.appendAnything()
return result
为此,我声明了两个自定义运算符:
infix operator +++{ associativity left precedence 95 }
func +++(lhs : [Section], rhs : Section) -> [Section] {
var result = lhs
result.append(rhs)
return result
}
infix operator +++= { associativity left precedence 95 }
func +++=(inout lhs : [Section], rhs : Section) {
lhs = lhs +++ rhs
}
当然还有 Section
结构的正确扩展:
extension Section {
mutating func appendTitle(title: String?) -> Section {
guard let unwrappedTitle = title
else { return self }
...
return self
}
mutating func appendPhotos(photos: [Photo]?) -> Section {
...
}
...
}
不幸的是,它没有按预期工作...
单独的 result +++= Section()
行是正确的,但是当我添加 .append
时它无法编译。
第一条消息是:Passing value of type '[Section]' to an inout parameter requires explicit '&'
然后我试着在result
前面放一个&
(但我从来没有为a += 1
做过)有第二条消息:Cannot use mutating member on immutable value: function call returns immutable value
因此,如果有人可以提供帮助,我们将不胜感激。
我正在使用 Swift 2.2 和 Xcode 7.
找到了!!!
这些方法不应该发生变化:
func appendTitle(title: String?) -> Section {
guard let unwrappedTitle = title
else { return self }
var result = self
... // Work with result instead of self!!!
return result
}
好吧,您找到了解决方案。太棒了!
但是请允许我解释一下这里到底发生了什么。
让我们仔细看看您之前的实施。
extension Section {
mutating func appendTitle(title: String?) -> Section {
guard let unwrappedTitle = title else { return self }
self.title = unwrappedTitle
return self
}
mutating func appendPhotos(photos: [Photo]?) -> Section {
guard let unwrappedPhotos = photos else { return self }
self.photos = unwrappedPhotos
return self
}
}
我们知道(至少现在)运算符重载函数绝对没问题,没有错。
给定的扩展正在改变参数的值,因此它应该是变异的,确实是。那为什么会报错:
"Cannot use mutating member on immutable value: function call returns immutable value"
原因:当调用"appendTitle"或"appendPhotos"时,它return是self的一个副本。不完全相同的引用!
这是因为 swift 处理不变性的方式。如果某个值类型的成员 属性 的值发生更改,则会创建一个副本,并将该值分配给相应的成员。
因此,当你写:Section().appendTitle(title).appendPhotos(photos)
Section()
returns 是 self 的副本,即 Section 的不可变版本。
因此,人们不能 appendTitle
它,或者一般不能改变它。
可以解决:
var section = Section()
section.appendTitle(title).appendPhotos(photos)
但现在 appendTitle(title)
return 是 self 的副本,即 Section 的不可变版本。同样的问题仍然存在。
您所做的是,您没有使用 self
更改任何内容,而是创建了一个新实例,这次是使用 var
的可变实例。对其进行变异并 return 对其进行编辑。现在它不会改变自己,因此它不必是 mutating func
.
您提供的解决方案在这里工作得很好,但实际上它是一个 hack,这里需要它来涵盖我现在在 Swift 中所说的 "bug"。
其他解决方案可以将 struct Section
更改为 class Section
。在那里您不需要创建 var result
,只需更改属性和 return.
这是我在 SO 上的第一个回答:)