Car*_*iel 9 f# struct class this seq
新的F#开发人员,长期以来的C#开发人员.作为学习F#的练习,我正在通过Eric Lippert关于图形着色的系列工作,从C#转换为F#.我目前正在做第二部分.
最初的C#在博文中 - 这是迄今为止的F#翻译 - 但是它没有编译:
type BitSet = struct
val bits : int
private new(b) = { bits = b }
static member Empty = BitSet(0)
member this.Contains (item:int) = (this.bits &&& (1<<< item)) <> 0
member this.Add (item:int) = BitSet(this.bits ||| (1 <<< item))
member this.Remove (item:int) = BitSet(this.bits &&& ~~~(1<<<item))
member this.Bits = seq {
for item in 0..31 do
if this.Contains(item) then
yield item
}
end
Run Code Online (Sandbox Code Playgroud)
这会产生非常神秘的错误"错误FS0406:byref-typed变量'this'以无效方式使用.Byrefs不能被闭包捕获或传递给内部函数"来自Bits:seq <int>的定义.
奇怪的是,将关键字"struct"更改为"class"会产生有效的代码.从C#的角度来看,这似乎是胡说八道,但我确信它背后有一个合理的原因.现在的问题是-如何应我写的位功能?为理解这一点,我需要理解的基本F#原则是什么?
我认为问题是this
引用被创建为对结构的当前值的引用,因此您可以修改结构(如果您需要并且结构是可变的).
这会导致内部问题,seq { .. }
因为这会在另一个类中生成代码,因此编译器无法将引用传递给"this"实例.
如果您为this
一个普通的局部变量赋值,那么代码工作正常:
member this.Bits =
let self = this
seq {
for item in 0..31 do
if self.Contains(item) then
yield item
}
Run Code Online (Sandbox Code Playgroud)
作为一种风格问题 - 我可能会使用隐式构造函数,这会使代码更短.我也更喜欢Struct
显式struct .. end
语法的属性,但这两者只是风格问题(我相信其他人会有不同的偏好).您可能会发现它只是作为替代或比较有用:
[<Struct>]
type BitSet private (bits:int) =
static member Empty = BitSet(0)
member this.Contains (item:int) = (bits &&& (1<<< item)) <> 0
member this.Add (item:int) = BitSet(bits ||| (1 <<< item))
member this.Remove (item:int) = BitSet(bits &&& ~~~(1<<<item))
member this.Bits =
let self = this
seq {
for item in 0..31 do
if self.Contains(item) then
yield item
}
Run Code Online (Sandbox Code Playgroud)
为了解决你的观点:
这会产生非常神秘的错误"错误FS0406:byref-typed变量'this'以无效的方式使用.Byrefs不能被闭包捕获或传递给内部函数"来自Bits:seq的定义.奇怪的是,将关键字"struct"更改为"class"会产生有效的代码.从C#的角度来看,这似乎是无稽之谈
从C#的角度来看,这似乎不是废话.这里:
struct Mutable
{
public int x;
public void DoIt()
{
Action a = () => {
this.x = 123;
};
}
}
Run Code Online (Sandbox Code Playgroud)
您将在该程序中得到相同的错误,以及有用的建议,您可以通过将其复制到本地来捕获"this"值.
这是三个事实的结果:首先this
在结构中S
是类型ref S
,而不是S
,第二,捕获变量,而不是值,第三,.NET类型系统不允许长期存储ref变量存储池,又称GC'd堆.Refs只能用于短期存储池:堆栈或寄存器.
这三个事实共同意味着你不能this
以任何可以比激活更长时间存活的方式存储在结构中,但这正是我们在创建委托时所需要的; 代表将在长期池中.