F#中的类级别和成员级别自我标识符之间的区别?

时间:2017-06-20 21:18:02

标签: f#

F#中的类级别和成员级别自我标识符之间是否存在语义差异?例如,考虑这个类:

type MyClass2(dataIn) as self =
    let data = dataIn
    do
        self.PrintMessage()
    member this.Data = data
    member this.PrintMessage() =
        printfn "Creating MyClass2 with Data %d" this.Data

与本课程相对应:

type MyClass2(dataIn) as self =
    let data = dataIn
    do
        self.PrintMessage()
    member this.Data = data
    member this.PrintMessage() =
        printfn "Creating MyClass2 with Data %d" self.Data

唯一的区别是PrintMessage的实现在一个中引用this而在另一个中引用self。语义有什么不同吗?如果没有,是否有一种风格上的理由偏爱另一种呢?

2 个答案:

答案 0 :(得分:5)

两者之间没有真正的语义差异。根据经验,我建议您使用第一个示例 - 更喜欢范围更接近的标识符,以便以后更容易阅读和重构代码。作为旁注,人们通常会将this用于类和成员级标识符,在这种情况下,成员级别的一个会影响类级别。

在这种情况下,查看像ILSpy这样的反汇编程序中的编译代码很有用。如果你这样做,你会发现唯一的区别是self.Data案例中插入的额外空检查。

另一方面,使用类级别标识符的类与不使用类级别标识符的类之间存在差异(一系列初始化检查将插入到所有类成员中)。如果可能的话,最好避免使用它们,并且可以将您的示例重写为不需要它。

答案 1 :(得分:4)

如scrwtp所述,this似乎是一种常用的标识符,这是我的偏好。另一个非常常见的是x。当它在整个类中多次使用时,我倾向于使用类级别标识符,当然,当它在构造函数中使用时。在这些情况下,我会使用__(两个下划线)作为成员级别标识符,以表示该值被忽略。您不能使用_并且实际上忽略它,因为它是编译错误,但是linting工具通常会将__视为同一件事,并避免向您发出有关未使用标识符的警告。

添加类级别标识符但不使用它时会收到警告:

  

递归对象引用'self'未使用。递归对象引用的存在将运行时初始化检查添加到此类和派生类型中的成员。请考虑删除此递归对象引用。

考虑以下代码:

type MyClass() =
    member self.X = self

type MyClassAsSelf() as self =
    member __.X = self

type MyClassAsSelfUnused() as self = // <-- warning here
    member __.X = ()

这是编译/反编译后这些类的样子:

public class MyClass
{
    public Program.MyClass X
    {
        get
        {
            return this;
        }
    }

    public MyClass() : this()
    {
    }
}
public class MyClassAsSelf
{
    internal FSharpRef<Program.MyClassAsSelf> self = new FSharpRef<Program.MyClassAsSelf>(null);

    internal int init@22;

    public Program.MyClassAsSelf X
    {
        get
        {
            if (this.init@22 < 1)
            {
                LanguagePrimitives.IntrinsicFunctions.FailInit();
            }
            return LanguagePrimitives.IntrinsicFunctions.CheckThis<Program.MyClassAsSelf>(this.self.contents);
        }
    }

    public MyClassAsSelf()
    {
        FSharpRef<Program.MyClassAsSelf> self = this.self;
        this..ctor();
        this.self.contents = this;
        this.init@22 = 1;
    }
}
public class MyClassAsSelfUnused
{
    internal int init@25-1;

    public Unit X
    {
        get
        {
            if (this.init@25-1 < 1)
            {
                LanguagePrimitives.IntrinsicFunctions.FailInit();
            }
        }
    }

    public MyClassAsSelfUnused()
    {
        FSharpRef<Program.MyClassAsSelfUnused> self = new FSharpRef<Program.MyClassAsSelfUnused>(null);
        FSharpRef<Program.MyClassAsSelfUnused> self2 = self2;
        this..ctor();
        self.contents = this;
        this.init@25-1 = 1;
    }
}

请注意,检查构造函数中是否已设置变量。如果检查失败,则调用一个函数:LanguagePrimitives.IntrinsicFunctions.FailInit()。这是抛出的异常:

  

System.InvalidOperationException:对象或值的初始化导致在完全初始化之前递归访问对象或值。

我想警告只是为了避免不必要的运行时检查的轻微开销。但是,我不知道如何构造抛出错误的情况,所以我不知道检查的确切目的。也许其他人可以阐明这一点?