【问题标题】:Call base member in lambda function from inherited class constructor?从继承的类构造函数调用lambda函数中的基成员?
【发布时间】:2014-10-13 17:58:13
【问题描述】:

我有一个基类

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 绑定中访问。

【问题讨论】:

  • Param 属性的类型是什么? DoSomething() 的返回值是多少?我无法重现你的情况。它似乎适用于简单的属性

标签: f#


【解决方案1】:

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

这行得通:

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

但这避免了你提到的重复:

do 
  base.DoSomething()

这就是绊倒你的原因——构造函数不能有返回值。如果序列中的最后一条语句有返回值,F# 假定函数返回值/类型是什么。但是,如果明确定义,这不能与方法签名相反。因此,F# 要求您在这种情况下明确说明您的意图。如果您的意图是丢弃 base.DoSomething() 的返回,请使用 |> ignore 的管道到忽略运算符组合,如下所示:

do
  base.DoSomething() |> ignore

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

公平地说,这并不是很明显,除非您考虑到以下几点: compiles all such statements into the primary constructor。如果您查看 constructor overloading 的 F# OO 样式,可能会更清楚 unit * 签名的来源。

通常,隐式定义返回值会使编写函数的体验更加流畅......正如 Rich Hickey 所说,现在只是熟悉了。

  • 单位在其他语言中的另一个名称是 void

更新

可能是编译器约束以一种过分急切的方式应用,或者可能在幕后,do 在解开并由编译器应用到构造函数之前被定义为一个闭包。闭包可以看到 this,但是方法/构造函数会得到一个 this 和一个 base。这不是很直观是吗?似乎您发现了需要打磨的粗糙边缘。请考虑制作feature request。 F# 现在是完全开源的,因此值得记录这样的案例。

我对此做了一个简短的练习。虽然 this 可以在构造函数中使用,但当有覆盖时,这还​​不够。我认为以下可能是一个不错的前进方式(请参阅末尾的 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")

【讨论】:

  • 谢谢。我之前的问题不是很清楚,我已经更新了。但是,您的回答仍然很有启发性,将来肯定会对我有所帮助。
  • @colinfang:是的。我做了一个更新作为回应。请参阅 new() 部分。我认为这可能是获得您想要的东西的一种方式。
【解决方案2】:

这行得通:

type MyInherited2() =
    inherit MyBase()

    let p = base.Param

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

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

但是你说的继承感觉很奇怪是对的。这是 F# 中的一大堆蠕虫,如果你不能完全避免它,最好让它保持简单。

【讨论】:

    【解决方案3】:

    无法复制。例如,这对我有用:

    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()
    

    【讨论】:

      猜你喜欢
      • 2018-03-31
      • 1970-01-01
      • 2015-03-21
      • 2013-05-07
      • 1970-01-01
      • 2013-02-12
      • 2015-12-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多