[<Struct>] type TestStruct = val mutable X: int new(x) = {_x = x} member this.Set(x) = this._x <- x this let a = TestStruct(2) let c() = let b = a.Set 10 b.X c();;
代码运行结果为10(我们想要的结果)。众所周知,类的子函数,其背后的实现是一个带对象输入的公共函数,调用a.Set(x),等效于执行伪代码:
Set(a, x) 。
由此可见,当我们绑定 let a = TestStruct(2),调用a.Set(x) 的时候,传递到Set函数中的this,其实是个复制值。
而当我们绑定 let mutable a = TestStruct(2),调用a.Set(x) 的时候,传递到Set函数中的this,却是a本身。
虽然,当我们在结构中使用mutable 的时候,可以使用返回this的方式,返回一个新的结构值。这也很符合函数编程的规范。但是如果可变数值使
用的是引用单元格,而不是mutable 的话。表现又略微不同:
mutable 方式,a值和Set返回值不同,一个表示原始值,另一个表示修改后的值;
引用单元格方式,a就是修改后的值。
[<Struct>] type TestStruct = val _x: int ref new(x) = {_x = ref x} member this.Set(x) = this._x := x member this.X = !this._x; let a = TestStruct(2) let c() = a.Set 10 a.X c();;
以上这段代码,可以得到正确值10,原理很简单,int ref实际上是一个类,所以结构复制后,其对应的对象都是同一个。
所以用户仍然无法从习惯风格本身理解 TestStruct.Set(x)函数的行为。此外.Net库里面常用的一个StringBiulder类,它的行为特征,则完全符合引用单元格方式。所以结构中,mutable 的使用要尤其审慎。毕竟,一个结构,如果let绑定使用和let mutable绑定使用,两者之间行为不一致的话,利用这种特性写出的代码,未免太奇淫技巧一点,徒增读代码的难度。