F# struct 成员引用 'this' 导致错误
F# struct member referencing 'this' results in an error
F# 开发新手,C# 开发老手。作为学习 F# 的练习,我正在学习 Eric Lippert 的图形着色系列,将他的 C# 翻译成 F#。我目前正在 part two.
原始 C# 在博客中 post - 这是到目前为止的 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
这会根据 Bits:seq 的定义产生非常神秘的错误 "error FS0406: The byref-typed variable 'this' is used in an invalid way. Byrefs cannot be captured by closures or passed to inner functions"。
奇怪的是,将关键字 "struct" 更改为 "class" 会生成有效代码。从 C# 的角度来看,这似乎是无稽之谈,但我确信它背后有一个正当的理由。问题是 - 应该 如何编写 Bits 函数?我需要了解什么基础 F# 原则才能使它有意义?
我认为问题在于 this
引用是作为对结构当前值的引用创建的,因此您可以修改结构(如果需要并且结构是可变的)。
这会导致 seq { .. }
内部出现问题,因为这会在另一个 class 内部生成代码,因此编译器无法将引用传递给 "this" 实例。
如果将 this
值赋给一个普通的局部变量,那么代码可以正常工作:
member this.Bits =
let self = this
seq {
for item in 0..31 do
if self.Contains(item) then
yield item
}
作为风格问题 - 我可能会使用隐式构造函数,这会使代码更短一些。我也更喜欢 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
}
this
对于 struct
和 class
意味着不同的东西,因为它们在内存中的表示方式不同。 C# 掩盖了细节并使其正常工作,而 F# 决定您需要自己处理差异。
正确的解决方案是将您关心的值缓存在局部变量中,以避免尝试在 seq
块中使用 this
。您可以像 Tomas 显示的那样缓存整个对象,或者只缓存 bits
值并内联 Contains
调用。
在这里解决你的问题:
This produces the very mysterious error "error FS0406: The byref-typed variable 'this' is used in an invalid way. Byrefs cannot be captured by closures or passed to inner functions" from the definition of Bits:seq.
Curiously, changing the keyword "struct" to "class" results in valid code. Coming from a C# perspective, this seems like nonsense
从 C# 的角度来看,这应该不是胡说八道。这里:
struct Mutable
{
public int x;
public void DoIt()
{
Action a = () => {
this.x = 123;
};
}
}
您将在该程序中遇到相同的错误,并得到有用的建议,您可以通过将 "this" 复制到本地来按值捕获它。
这是三个事实的结果:首先,结构 S
中的 this
是 ref S
类型,而不是 S
,其次,变量不是值,被捕获,第三,.NET 类型系统不允许在长期存储池(也称为 GC 堆)上存储 ref 变量。 Refs 只能放在短期存储池中:堆栈或寄存器。
这三个事实一起意味着您不能以任何方式将 this
存储在结构中,这可能会比激活时间更长,但这正是我们在创建委托时所需要的;代表将加入长期池。
F# 开发新手,C# 开发老手。作为学习 F# 的练习,我正在学习 Eric Lippert 的图形着色系列,将他的 C# 翻译成 F#。我目前正在 part two.
原始 C# 在博客中 post - 这是到目前为止的 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
这会根据 Bits:seq
奇怪的是,将关键字 "struct" 更改为 "class" 会生成有效代码。从 C# 的角度来看,这似乎是无稽之谈,但我确信它背后有一个正当的理由。问题是 - 应该 如何编写 Bits 函数?我需要了解什么基础 F# 原则才能使它有意义?
我认为问题在于 this
引用是作为对结构当前值的引用创建的,因此您可以修改结构(如果需要并且结构是可变的)。
这会导致 seq { .. }
内部出现问题,因为这会在另一个 class 内部生成代码,因此编译器无法将引用传递给 "this" 实例。
如果将 this
值赋给一个普通的局部变量,那么代码可以正常工作:
member this.Bits =
let self = this
seq {
for item in 0..31 do
if self.Contains(item) then
yield item
}
作为风格问题 - 我可能会使用隐式构造函数,这会使代码更短一些。我也更喜欢 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
}
this
对于 struct
和 class
意味着不同的东西,因为它们在内存中的表示方式不同。 C# 掩盖了细节并使其正常工作,而 F# 决定您需要自己处理差异。
正确的解决方案是将您关心的值缓存在局部变量中,以避免尝试在 seq
块中使用 this
。您可以像 Tomas 显示的那样缓存整个对象,或者只缓存 bits
值并内联 Contains
调用。
在这里解决你的问题:
This produces the very mysterious error "error FS0406: The byref-typed variable 'this' is used in an invalid way. Byrefs cannot be captured by closures or passed to inner functions" from the definition of Bits:seq. Curiously, changing the keyword "struct" to "class" results in valid code. Coming from a C# perspective, this seems like nonsense
从 C# 的角度来看,这应该不是胡说八道。这里:
struct Mutable
{
public int x;
public void DoIt()
{
Action a = () => {
this.x = 123;
};
}
}
您将在该程序中遇到相同的错误,并得到有用的建议,您可以通过将 "this" 复制到本地来按值捕获它。
这是三个事实的结果:首先,结构 S
中的 this
是 ref S
类型,而不是 S
,其次,变量不是值,被捕获,第三,.NET 类型系统不允许在长期存储池(也称为 GC 堆)上存储 ref 变量。 Refs 只能放在短期存储池中:堆栈或寄存器。
这三个事实一起意味着您不能以任何方式将 this
存储在结构中,这可能会比激活时间更长,但这正是我们在创建委托时所需要的;代表将加入长期池。