简体   繁体   English

成员val和F#中的成员有什么区别?

[英]What is the difference between member val and member this in F#?

When I created a class containing a generic, mutable .NET Stack in F# like the example below, that stack ignores anything I push onto it. 当我在F#中创建一个包含通用的,可变的.NET Stack的类时,如下例所示,该堆栈忽略了我推送它的任何东西。

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?!

Yet if I make the stack mutable via a property: 然而,如果我通过属性使堆栈可变:

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]"

Everything magically works like I'd expect. 一切都像我期望的那样神奇地起作用。

What on earth is going on here? 这到底是怎么回事? My understanding of immutability from previous languages (C# mostly) would say that even though the stack in the first example is an immutable member, that immutablity should only go as far the reference (aka I shouldn't be able to reassign Stack itself). 我对以前语言(大多数是C#)的不变性的理解会说,即使第一个例子中的堆栈是一个不可变成员,这个不可变性应该只是参考(我不应该重新分配Stack本身)。 I should still be able to push values to/from it. 我仍然可以将值推送到/从中推送。 What am I missing, and if trying to mutate that stack is the wrong thing, why doesn't it throw an exception or a compile error? 我错过了什么,如果试图改变该堆栈是错误的,为什么不抛出异常或编译错误?

If you try to compile the first version, and then use eg Reflector to decompile it to C#, you'll see that the stack member is defined like this: 如果您尝试编译第一个版本,然后使用例如Reflector将其反编译为C#,您将看到堆栈成员的定义如下:

public class Interp
{
    public Stack<int> stack
    {
        get { return new Stack<int>(); }
    }

    // Other members omitted for clarity...
}

As you can see, this is also valid C# code, but obviously not what you want. 如您所见,这也是有效的C#代码,但显然不是您想要的。

The second version cross-compiles to something like this: 第二个版本交叉编译成这样的东西:

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...
}

This seems more like what you'd want a property to do. 这看起来更像是你想要财产做的事情。

A more idiomatic way to do what you want is this: 做一个你想要的更惯用的方法是:

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

If you don't need to expose the stack externally, omit the next to last line. 如果您不需要在外部公开堆栈,请省略最后一行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM