话说 type 之 let 绑定与 val 显式字段 F#

今天和大家玩个找不同的游戏,先看看下面两段代码有什么不同,嘿嘿~。~

type A() =
let left = 0
member x.Left = left
type B =
val private left : int
new () = { left = 0 }
member x.Left = x.left

(有人要说了,类名不同,嘿嘿~。~)额 ><!

其实我是想让大家注意

    member x.Left = left

    member x.Left = x.left

,看出了没?类B多了个 x.

对于类B来说,如果去掉这个 x. 会报下面这个错误。

The value or constructor 'left' is not defined

难道说,let定义出的字段和val定义出的字段对于类的成员内部的可见性是不一样的?

带着这个疑问,我研究了一下这两个类通过 .net reflector 转换后生成的 C# 代码,先贴出来:

[Serializable, CompilationMapping(SourceConstructFlags.ObjectType)]
public class A
{
// Fields
internal int left = 0;

// Properties
public int Left
{
get
{
return this.left;
}
}
}
[Serializable, CompilationMapping(SourceConstructFlags.ObjectType)]
public class B
{
// Fields
internal int left@ = 0;

// Properties
[CompilationMapping(SourceConstructFlags.Field, 0)]
internal int left
{
get
{
return this.left@;
}
}

public int Left
{
get
{
return this.left@;
}
}
}

继续找不同,呵呵。

我们看到的 class A 没啥特别的,class B 却有些许不同:

在这里,我们先来理解一下 let 和 val 的意思

MSDN 对 类中的 let 绑定的解释为: 可以通过使用类定义中的 let 绑定,为 F# 类定义私有字段和私有函数。
MSDN 对类中的 val 的解释为: val 关键字用于在类类型或结构类型中声明字段,而无需将其初始化。通过此方式声明的字段称作“显式字段”。

这两段解释在C#代码中体现的非常明显了,尤其是val,它其实已经定义好了一个 left 属性。如果去掉 type B 中 left 前面的 private, 则就可以将属性暴露出来了。

但是令人费解的是,为什么我们自己定义的属性 Left 中的代码仍然是下面这样呢?

return this.left@;

像下面这样的代码才对啊

return this.left

其实,我们写的代码确实表达的是第二种情况,编译器对我们的代码进行了优化后,直接将 left@ 输出了,才形成了第一个代码。

============================================================

最后,让我们看看 member 的语法和 type B 中那个蛋疼的 x.left。我先上个类

type A() =
member x.Hi = "Hi"
member y.ReHi = y.Hi

晕,怎么一会x一会y的? 结合 member 的语法看看吧。

// Property that has get only.
[ attributes ]
[ static ] member [accessibility-modifier] [self-identifier.]PropertyName =
get-function-body

其实这里的x. y. 对应的是 [self-identifier.] 。如果不写,会返回一个错误

This instance member needs a parameter to represent the object being invoked. Make the member static or use the notation 'member x.Member(args) = ...'

白话一点说,就是你在 member 成员内部的代码中,需要给当前的类起个名字,常见的有起 this. 的,还有为了和C#中的 this. 不混淆而起 self. 的,然后要引用其他的成员时,需要用这个名字来指明。

(但是如果是 static member,则不需要定义这个名字)

而如果使用类内部的字段是,是不需要用这个 self-identifier 来指明的。

所以,type B 中的 member x.Left = x.left 返回的是 left 属性而不是 left 字段。

============================================================

最后,不得不说,这篇文章研究的东西有点蛋疼,不过您是不是也有所得呢?

原文地址:https://www.cnblogs.com/softcat/p/2339323.html