从继承的类构造函数调用lambda函数中的基本成员?

时间:2014-10-13 17:58:14

标签: f#

我有一个基类

type MyBase() =
    let param = myfun()
    member this.Param = param


type MyInherited() =
    inherit MyBase()
    do
         base.Param.DoSomething() // cannot access base in let or do binding.

我想在继承的对象实例化期间准确地调用DoSomething一次。但在这里我不被允许。那我该怎么办?

我是否必须创建方法

     member this.DoSomething() = base.Param.DoSomething()

并在构造函数中调用它?

type MyInherited() as self =
    inherit MyBase()
    do
         self.DoSomething()

感觉有点怪异,并且重复

更新

我最初的简化示例不合适。检查以下内容:

type MyBase() =
    let param = "test"
    member this.Param = param

type MyInherited() =
    inherit MyBase()
    do
       (fun () -> base.Param) () |> ignore  // Doesn't work, 
   // A protected member is called or 'base' is being used. 
   // This is only allowed in the direct implementation of members
   // since they could escape their object scope.


type MyInherited() as self =
    inherit MyBase()
    do
       (fun () -> self.Param) () |> ignore  // Works

现在实际上它好多了,我需要做的就是使用self代替base ...(我不需要重新定义Param,因为它是已经继承了。)

这里解释了F#有这种限制的原因:

why is base only possible in private members?

但我仍然不清楚为什么base不能在闭包中使用,尽管可以通过简单的let绑定来访问它。

3 个答案:

答案 0 :(得分:6)

您的初始代码完全有效。您不必首先定义一个调用基地的方法。

这有效:

do 
  this.DoSomething()
member this.DoSomething() = base.DoSomething() 

但这可以避免重复,就像你提到的那样:

do 
  base.DoSomething()

这就是绊倒你的东西 - 构造函数不能有返回值。如果序列中的最后一个语句有返回,则F#假定该函数返回该值/类型的任何值。但是,如果明确定义,这不能与方法签名相反。所以F#要求你在这种情况下明确你的意图。如果您打算丢弃base.DoSomething()的返回,请使用 |> 忽略的pipe-to-ignore运算符组合,如下所示:

do
  base.DoSomething() |> ignore

让我们换一种方式,如果 功能'目的返回一个没有副作用的值,如果这个值没有被使用,那么我们可以安全地得出结论,该函数不需要在构造函数。因此,编译器/交互式环境会向您发出警告。这就是F#在这里所鼓励的。

公平地说,除非您考虑以下因素,否则不会立即显而易见:执行 compiles all such statements into the primary constructor。如果您查看constructor overloading的F#OO风格,那么单元 *签名来自哪里可能更清楚。

通常情况下,隐式定义的返回值会使写入功能变得更加流畅......正如Rich Hickey所说,现在只是熟悉了。

  • 其他语言单位的另一个名称是无效

更新

可能正在以过度的方式应用编译器约束,或者可能在幕后将do定义为闭包,然后由编译器解开并应用于构造函数。闭包将能够看到 this ,但是方法/构造函数会获得 this base 。这不是很直观吗?看起来你已经找到了需要打磨的粗糙边缘。请考虑制作一个feature request。 F#现在是完全开源的,因此值得记录这样的案例。

我做了一个简短的练习。虽然这个可以在构造函数中使用,但在覆盖时这是不够的。我认为以下可能是一个不错的前进方向(最后参见 new()块。)

[<AbstractClass>]
type MyBase()  =
    let letValue = "let value"
    abstract Method : unit -> string
    default this.Method() = "test"
    member this.Method2() = "Param2"

type MyInherited(param : string) as this =
    inherit MyBase()

    // let localLetValue() = this.letValue      // fails.  let values are private members

    do
      //(fun () -> base.Param) () |> ignore     // Error: The 'base' keyword is used in an invalid way. Base calls cannot be used in closures. Consider using a private member to make base calls.  
      (fun () -> this.Method) () |> ignore       // succeeds -- takes local
      (fun () -> this.base_Method) () |> ignore  // succeeds -- takes base
      (fun () -> this.Method2) () |> ignore      // succeeds -- takes base

    override this.Method() = "ABCdefHIJ"
    member this.base_Method() = base.Method()

    new() as this =
      let a = base.Method()                     // succeeds
      let b = this.Method()                     // succeeds
      MyInherited("some value")

答案 1 :(得分:2)

这有效:

type MyInherited2() =
    inherit MyBase()

    let p = base.Param

    do
        (fun () -> p) () |> ignore

MyInherited示例中的问题是由于在闭包中捕获了base.Param这一事实。这适用于let绑定,因为对于所有意图和目的,对象构造函数中的let绑定是成员的直接实现。

但你对继承的感觉很奇怪。它是F#中的一大块蠕虫,如果你无法完全避免它,最好让它变得简单。

答案 2 :(得分:1)

无法重现。例如,这对我有用:

type MyBase() =
    let param = "test"
    let param' = fun _ -> printfn "test'"
    member this.Param = param
    member this.Param' = param'

type MyInherited() =
    inherit MyBase()
    do
       printfn "%s" (base.Param.ToString()) 
       base.Param'()

let inh = new MyInherited()