F# 中的成员 val 和成员 this 有什么区别?
What is the difference between member val and member this in F#?
当我在 F# 中创建一个包含通用可变 .NET 堆栈的 class 时(如下例所示),该堆栈会忽略我向其推送的任何内容。
open System.Collections.Generic
type Interp(code: int array) =
member val PC = 0 with get, set
member this.stack: Stack<int> = new Stack<int>()
member this.Code = code
let interp = Interp([|1;2;3|])
interp.stack.Push(1)
printfn "%A" interp.stack // prints "seq[]" WAT?!
然而,如果我通过 属性:
使堆栈可变
open System.Collections.Generic
type Interp(code: int array) =
member val PC = 0 with get, set
member val stack: Stack<int> = new Stack<int>() with get, set
member this.Code = code
let interp = Interp([|1;2;3|])
interp.stack.Push(1)
printfn "%A" interp.stack // prints "seq[1]"
一切都像我期望的那样神奇地工作。
这到底是怎么回事?我对以前语言(主要是 C#)的不可变性的理解会说,即使第一个示例中的堆栈是一个不可变成员,不可变性应该只达到引用(也就是我不应该能够重新分配 Stack 本身)。我应该仍然能够 push values to/from 它。我错过了什么,如果尝试改变该堆栈是错误的,为什么它不抛出异常或编译错误?
如果您尝试编译第一个版本,然后使用例如Reflector反编译成C#,你会看到stack member是这样定义的:
public class Interp
{
public Stack<int> stack
{
get { return new Stack<int>(); }
}
// Other members omitted for clarity...
}
如您所见,这也是有效的 C# 代码,但显然不是您想要的。
第二个版本交叉编译成这样:
public class Interp
{
internal int[] code;
internal Stack<int> stack@;
public Interp(int[] code) : this()
{
this.code = code;
this.stack@ = new Stack<int>();
}
public Stack<int> stack
{
get { return this.stack@; }
set { this.stack@ = value; }
}
// Other members omitted for clarity...
}
这看起来更像是您希望 属性 做的事情。
做你想做的更惯用的方法是:
open System.Collections.Generic
type Interp(code: int array) =
let stack = Stack<int>()
member val PC = 0 with get, set
member this.Stack = stack
member this.Code = code
如果不需要在外部公开堆栈,请省略倒数第二行。
当我在 F# 中创建一个包含通用可变 .NET 堆栈的 class 时(如下例所示),该堆栈会忽略我向其推送的任何内容。
open System.Collections.Generic
type Interp(code: int array) =
member val PC = 0 with get, set
member this.stack: Stack<int> = new Stack<int>()
member this.Code = code
let interp = Interp([|1;2;3|])
interp.stack.Push(1)
printfn "%A" interp.stack // prints "seq[]" WAT?!
然而,如果我通过 属性:
使堆栈可变open System.Collections.Generic
type Interp(code: int array) =
member val PC = 0 with get, set
member val stack: Stack<int> = new Stack<int>() with get, set
member this.Code = code
let interp = Interp([|1;2;3|])
interp.stack.Push(1)
printfn "%A" interp.stack // prints "seq[1]"
一切都像我期望的那样神奇地工作。
这到底是怎么回事?我对以前语言(主要是 C#)的不可变性的理解会说,即使第一个示例中的堆栈是一个不可变成员,不可变性应该只达到引用(也就是我不应该能够重新分配 Stack 本身)。我应该仍然能够 push values to/from 它。我错过了什么,如果尝试改变该堆栈是错误的,为什么它不抛出异常或编译错误?
如果您尝试编译第一个版本,然后使用例如Reflector反编译成C#,你会看到stack member是这样定义的:
public class Interp
{
public Stack<int> stack
{
get { return new Stack<int>(); }
}
// Other members omitted for clarity...
}
如您所见,这也是有效的 C# 代码,但显然不是您想要的。
第二个版本交叉编译成这样:
public class Interp
{
internal int[] code;
internal Stack<int> stack@;
public Interp(int[] code) : this()
{
this.code = code;
this.stack@ = new Stack<int>();
}
public Stack<int> stack
{
get { return this.stack@; }
set { this.stack@ = value; }
}
// Other members omitted for clarity...
}
这看起来更像是您希望 属性 做的事情。
做你想做的更惯用的方法是:
open System.Collections.Generic
type Interp(code: int array) =
let stack = Stack<int>()
member val PC = 0 with get, set
member this.Stack = stack
member this.Code = code
如果不需要在外部公开堆栈,请省略倒数第二行。