最近我在Swift中尝试了赋值运算符并遇到了一个问题,我无法找到解决方案。
考虑以下架构:
struct Box<T> {
let value: T
}
struct MyStruct {
let myProperty: String
init(boxedProperty: Box<String>) {
self.myProperty = boxedProperty.value
}
}
看起来非常简单,但想象MyStruct
接受20个盒装属性 - 每个属性都需要通过访问value
实例的Box
属性来“取消装箱”。现在假设有MyStruct
的20个版本。
这将导致数百条.value
行,这是非常多的。相反,为了减少代码混乱,我想使用自定义赋值运算符,它隐式地“取消装箱”Box
并将其值赋给变量。
考虑这个简单的例子(没有选项支持等):
infix operator <|= {
associativity right
precedence 90
assignment
}
func <|= <T>(inout lhs: T, rhs: Box<T>) {
lhs = rhs.value
}
理想情况下,我想立即使用<|=
运算符,如下所示:
init(boxedProperty: Box<String>) {
self.myProperty <|= boxedProperty
}
但遗憾的是(而且非常可预测?),这不起作用,因为myProperty
变量在用于函数之前未被初始化:
error: variable 'self.myProperty' passed by reference before being initialized
self.myProperty <|= boxedProperty
^
我可以确保编译器我的assignment
运算符函数始终初始化变量(a.k.a。其lhs
操作数)吗?此外,如果您对不同的方法有所了解,请随时发表评论。
注意:实际案例更复杂,需要更多的“拆箱”,而不仅仅是访问value
结构的Box
属性。
答案 0 :(得分:2)
您不能按照您的描述方式执行此操作,因为赋值运算符使用表达式中的两个操作数,您可以使用未初始化的变量。那么使用返回盒装值的前缀运算符呢?
prefix operator <| { }
prefix func <|<T>(rhs: Box<T>) -> T {
return rhs.value
}
// ...
init(boxedProperty: Box<String>) {
self.myProperty = <|boxedProperty
}
答案 1 :(得分:1)
没有理由使用语法来实现您的目标。只需定义一个用“几行”封装“switch
语句”的函数。确保该功能与let
绑定,以便init()
内可以访问该功能:
struct Box<T> {
let value: T
init (t:T) {
self.value = t
}
}
struct MyStruct {
let myProp: String
let myUnboxer : Box<String> -> String = { (b:Box<String>) in
/* switch, 5-10 lines */
return b.value
}
init (bp:Box<String>) {
self.myProp = myUnboxer(bp)
}
}
行动中:
15> var ms = MyStruct(bp:Box(t:"abc"))
ms: MyStruct = {
myProp = "abc"
myUnboxer =
}
16> ms.myProp
$R0: String = "abc"
“费用”是myUnboxer
的“额外”实例变量,但当拆箱的详细信息变为特定结构时,这很快就会成为优势 - 此时您将初始化MyStruct
与unboxer一样。
对我来说,在一种具有闭包和一流功能的语言中,发明语法通常是错误的 - 除非有人主动定义子语言。使用特殊评估规则(又称“语法”)的语句会让人感到困惑。
答案 2 :(得分:0)
我使用枚举来存储对象的属性,如下所示:
enum TextFieldProps {
case StringValue(String)
case Editable(Bool)
}
然后我对NSTextField有一个扩展,其中包含一个名为assignProps
的函数,该函数采用TextFieldProps
数组:
func assignProps(tfp: [TextFieldProps]) {
tfp.reduce(self) { prop in
switch prop {
case .StringValue(let value):
self.stringValue = value
case .Editable(let value):
self.editable = value
}
return self
}
}
当我需要设置或更改NSTextField的属性时,允许我将TextFieldProps
的数组传递给assign
,使用reduce
一次更新所有内容
我认为你试图使用类似的东西,只有一个通用的Box
来保存它里面的值。也许有一种方法可以修改上面的技术以适应您的目的。如果不了解更多关于枚举结构和您正在使用的Box
ed值,很难给出明确的答案。
我可以说的是,如果您尝试设置20个属性的值,那么无论您是否使用自定义运算符,都会有20个分配调用。
当我使用Box
类型的对象(我有时会这样做)时,我有几个不同的自定义函数。其中一个叫做o
,用于&#34;打开&#34;:
func o(b: Box<T>) -> T {
return b.value
}
// Now you can use it like this:
init(b: Box<String>) {
self.property = o(b)
}
我知道它并没有直接回答您询问的有关自定义赋值运算符的问题,但是如果您将init标签缩短为b
并使用名为o
的函数,那么您就是&#39 ;我把整个事情归结为4个字符,o(b)
。对于我而言,o(b)
对我来说简直有效,直观地看起来像是opened box
的简写,所以代码不会太混乱。
您可能希望限制o
的范围,以便框架的后续用户可以根据需要使用o
作为变量名称。您实际上可以在o
MyStruct
函数中创建init
,如下所示:
struct MyStruct {
let myProperty: String
init(b: Box<String>) {
let o: Box<String> -> String = { $0.value }
self.myProperty = o(b)
}
}
o
现在是一个闭包,在init
函数内部定义,并且范围仅限于该函数。您仍然可以在代码中的任何其他位置使用o
作为变量名,而不会发生任何冲突。
答案 3 :(得分:0)
我认为这可以通过使用默认属性值来解决。例如。类似的东西:
struct Box<T> {
private let value: T? = nil
init(value: T) {
self.value = value
}
}
struct MyStruct {
let myProperty: String = ""
init(boxedProperty: Box<String>) {
self.myProperty <|= boxedProperty
}
}
infix operator <|= { associativity right precedence 90 assignment }
func <|= <T>(inout lhs: T, rhs: Box<T>) {
lhs = rhs.value!
}
var box = Box<String>(value: "foo")
var myStruct = MyStruct(boxedProperty: box)