当我有以下代码时:
[<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
本身的值。
对于非结构,引用和被引用对象之间存在重要区别:
引用和对象本身都是可变的。这样您就可以更改引用(即使其指向其他对象),也可以更改对象(即更改其字段的内容)。
但是,对于结构,不存在这种区别,因为没有引用:
这意味着,当您对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/