F#接口和属性

13

我正在尝试掌握F#,在此过程中我正在转换一些C#代码。我在接口中定义属性并在类型中实现它们时遇到了一些问题。

请考虑以下代码:

module File1

type IMyInterface =
    abstract member MyProp : bool with get, set
    abstract member MyMethod : unit -> unit

type MyType() = 
    interface IMyInterface with
        member val MyProp = true with get, set
        member self.MyMethod() = if MyProp then () else ()

F# 属性文档中似乎说明了我在 MyType 中实现 MyProp 的方式是正确的,但是编译器报告“值或构造函数 'MyProp' 未定义”。有什么想法吗?


2
尝试使用 self.MyProp - 你需要 this 限定符 ;) - Random Dev
@Carsten,似乎自动属性无法正常工作。 - Eyvind
3个回答

20

要访问(显式)接口中的属性,您需要将引用转换为接口类型的 self 引用:

type MyType() = 
    interface IMyInterface with
        member val MyProp = true with get, set
        member self.MyMethod() = if (self :> IMyInterface).MyProp then () else ()

如果你在C#中显式实现接口,也会遇到同样的错误,而且需要进行强制类型转换才能访问成员:

interface IMyInterface
{
    bool MyProp { get; set; }
    void MyMethod();
}

class MyType : IMyInterface
{
    bool IMyInterface.MyProp { get; set; }

    void IMyInterface.MyMethod()
    {
        if (((IMyInterface)this).MyProp) { }
    }
}

啊哈!我之前并不知道这是一个显式接口实现。有没有一种隐式实现的方法? - Eyvind
3
据我所知,在 F# 中,所有接口都必须显式实现。Scott在这里很好地解释了http://fsharpforfunandprofit.com/posts/interfaces/。 - Kevin
1
没问题,是的,在F#中接口只能显式实现。 - Phillip Trelford

13

如果你只是访问接口,那么你不需要在类型本身中定义成员。 如果你想要简洁,Phil 的回答是很好的,但另一种方法我喜欢使用“let-bound”值而不是成员——对于更复杂的代码,类型推断更好,并且它们通常比成员更容易处理。

type MyType() = 
    let mutable myProp = true 
    let myMethod() = if myProp then () else ()

    interface IMyInterface with
        member __.MyProp with get() = myProp and set v = myProp <- v
        member __.MyMethod() = myMethod()

我认为,与成员版本相比,该代码更加清晰,因为在type MyType() as self中的self标签仅需要用于访问成员 -- 绑定值可以直接从接口访问。


1
我喜欢这么想:(a) 我使用let绑定的值和函数来完成所有真正的工作(相当于私有方法),然后 (b) 通过一个或多个接口公开其中一些功能。 接口只是指向“真实”代码的代理。 - Grundoon

5

这里有一个可用的版本:

type IMyInterface =
    abstract member MyProp : bool with get, set
    abstract member MyMethod : unit -> unit

type MyType() = 
    member val MyProp = true with get, set
    member self.MyMethod() = if self.MyProp then () else ()
    interface IMyInterface with
        member self.MyProp with get () = self.MyProp and set v = self.MyProp <- v
        member self.MyMethod() = self.MyMethod()

请注意,我包括了显式成员,因为你肯定会错过它们 ;)

1
"MyProp" 是一个自动属性 - 它只是接口实现,覆盖了这个属性 - 也许有更短的方法,但老实说,我尽量不使用可变值,所以我不确定 ;) - Random Dev
2
根据我的经验,F#在函数式编程时非常优雅,但在面向对象编程时可能会变得“混乱”。这促使你去追求更好的函数式设计 :) - Mark Seemann
1
它只是混乱的,因为你不会得到隐式接口,所以你要么必须做某些事情两次(就像这里一样),要么你必须一直做(myType:> IMyInterface).MyProp - 不管怎样,正如Mark评论的那样,如果你尝试从C#中 1:1复制这些内容,你将不会感到满意。 - Random Dev
菲利普向你展示了如何不使用 - 顺便说一句:在我看来,平滑的互操作性并不是第一设计目标(但如果你小心的话通常会有)- F#在其类型使用上非常明确,这是一件好事。 - Random Dev
1
@Eyvind 我认为类型推断和隐式转换之间存在差异。F#会为您推断类型,就好像您已经明确注释了它们一样,但它不会将类隐式向上转换为接口或基类(有一定的例外)。它也不会将int隐式转换为float,甚至不会将int64隐式转换为int32!这就是Carsten所说的。在C#中,您可以做很多事情,而F#会抱怨并强制您明确表达意图。过了一段时间,您甚至会喜欢所有的抱怨! - Grundoon
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接