当我有以下代码时:

[<Struct>]
type Person = { mutable FirstName:string ; LastName : string}

let john = { FirstName = "John"; LastName = "Connor"}

john.FirstName <- "Sarah";

编译器提示“一个值必须是可变的,以便使内容突变”。但是,当我删除Struct属性时,它可以正常工作。为什么会这样 ?

最佳答案

这可以保护您免受几年前困扰C#世界的陷阱:结构是通过值传递的。

请注意,红色波浪状(如果您在IDE中)不是出现在FirstName下,而是出现在john下。编译器提示的不是更改john.FirstName的值,而是更改john本身的值。

对于非结构,引用和被引用对象之间存在重要区别:

f# - 在F#中不允许写入用于struct记录的可变属性。为什么?-LMLPHP

引用和对象本身都是可变的。这样您就可以更改引用(即使其指向其他对象),也可以更改对象(即更改其字段的内容)。

但是,对于结构,不存在这种区别,因为没有引用:

f# - 在F#中不允许写入用于struct记录的可变属性。为什么?-LMLPHP

这意味着,当您对john.FirstName进行突变时,您也对john本身进行了突变。他们是一样的。

因此,为了执行此突变,您还需要将john本身声明为可变的:

[<Struct>]
type Person = { mutable FirstName:string ; LastName : string}

let mutable john = { FirstName = "John"; LastName = "Connor"}

john.FirstName <- "Sarah"  // <-- works fine now

有关的进一步说明,请在C#中尝试以下操作:
struct Person
{
    public string FirstName;
    public string LastName;
}

class SomeClass
{
    public Person Person { get; } = new Person { FirstName = "John", LastName = "Smith" };
}

class Program
{
    static void Main( string[] args )
    {
        var c = new SomeClass();
        c.Person.FirstName = "Jack";
    }
}

IDE会很有帮助地在c.Person下划线,并告诉您“由于它不是变量,因此无法修改'SomeClass.Person'的返回值”。

这是为什么?每次编写c.Person时,即转换为调用属性getter,就像另一种返回Person的方法一样。但是因为Person是按值传递的,所以返回的Person每次都是不同的Person。 getter无法返回对同一对象的引用,因为不能有对结构的引用。因此,您对此返回值所做的任何更改都不会反射(reflect)在Person内的原始SomeClass中。

在这个有用的编译器错误出现之前,很多人会这样做:
c.Person.FirstName = "Jack"; // Why the F doesn't it change? Must be compiler bug!

我清楚地记得几乎每天都回答这个问题。那些日子! :-)

关于f# - 在F#中不允许写入用于struct记录的可变属性。为什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44877506/

10-17 00:37