【问题标题】:How to call the base F# method from a computation expression in an overridden method?如何从覆盖方法中的计算表达式调用基本 F# 方法?
【发布时间】:2020-01-27 07:03:05
【问题描述】:

如何从重写方法中的计算表达式调用基本 F# 方法?

这里是一个令人惊讶的问题的一些人为的例子。不要问我为什么需要 在这种特殊情况下,实际用例更有意义,但它是 难以代表。特别是async是无关紧要的,在实际中 案例我使用不同的计算表达式,甚至不需要爆炸 (!) 用于调用基本方法。但是症状是一样的。

// Base class with a virtual method
type T1 () =
    abstract Test : unit -> Async<unit>
    default _.Test () =
        async {
            printfn "In test"
        }

// Derived class with an overridden method calling the base in a computation expression
type T2 () =
    inherit T1 ()
    override _.Test () =
        async {
            do! base.Test ()
        }

let t2 = T2 ()
t2.Test () |> Async.RunSynchronously

以上代码无法编译:

error FS0405: 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.

这条神秘的信息是什么意思?我正在给一位公众成员打电话 直接实现它,就像编译器“想要”一样。

那么主要的问题是,在这种情况下如何调用基本方法?什么是 惯用的 F# 方式?

解决方法是可能的,例如:

type T1 () =
    abstract Test : unit -> Async<unit>
    default m.Test () =
        m.DoTest ()

    member _.DoTest () =
        async {
            printfn "In test"
        }

type T2 () =
    inherit T1 ()
    override m.Test () =
        async {
            do! m.DoTest ()
        }

let t2 = T2 ()
t2.Test () |> Async.RunSynchronously

我并不是真的在寻找解决方法,但看到其他人会很有趣。

【问题讨论】:

    标签: f#


    【解决方案1】:

    这里的问题是async 中的代码被编译成一个单独的类,然后它存在于当前类之外,因此它不能以典型的方式调用基方法。

    在这种情况下,您可以在async 块之前调用该方法。这是可行的,因为这只是普通方法体的一部分。该方法返回async,直到你用do!调用它才真正执行

    type T2 () =
        inherit T1 ()
        override __.Test () =
            let baseTest = base.Test ()
            async {
                do! baseTest
            }
    

    这是一个不错但有限的技巧。当您需要首先计算一些参数然后将它们传递给Test 时,它将不起作用(因为您不能将baseTest 设为函数 - 它必须是一个值)。在这种情况下,唯一的选择是定义一个调用基本方法的辅助方法:

    type T2 () =
        inherit T1 ()
        member private x.BaseTest() = 
            base.Test ()
        override x.Test () =
            async {
                do! x.BaseTest()
            }
    

    我认为这比您的解决方法要好一些,因为它不需要基类中的任何虚拟方法。 base.Test 调用将从派生类的私有方法中工作。

    【讨论】:

    • 感谢您提供的详细信息和提示。您的解决方法通常更好,特别是它不需要基类调整,但并非总是可行。对于具有相同问题的许多派生类,我的解决方法是“更好”。过一段时间我会把它标记为答案,我怀疑有更好的方法......
    • 接受,再次感谢。所以...我理解“为什么”,但 F# 以这种方式设计“可以”吗?
    • 我认为这不是很好,但我不确定是否有办法让它发挥作用。这只是 OOP 和 F# 之间那些不幸的交互之一。主要是,我不认为让这项工作具有如此高的优先级 - 我认为大多数将 OOP 与 F# 一起使用的人使用接口而不是实现继承,所以这是一个相对罕见的问题。
    • 这是一个有用的解决方法,但它让我想知道在计算表达式中调用私有成员是如何合法的。似乎它应该会导致与 base 首先发生的相同类型的问题。
    【解决方案2】:

    解决方法是使用自标识符:

    type T2 () as self =
        inherit T1 ()
        override _.Test () =
            async {
                do! self.Test ()
            }
    

    我认为这里不允许base 的原因是在创建T1 时,它需要了解T1。因此T1 成为递归定义,F# 默认不支持递归定义。 as self 使 T2 递归定义并允许这样做。

    【讨论】:

    • 我认为它会导致堆栈溢出。 self.Test 只是调用相同的方法,而不是基本方法。
    • 好的,抱歉,我最初没有遇到问题。如果你覆盖它,我怀疑你是否可以访问默认方法。
    • 在计算表达式之外从Test调用base.Test没问题。
    猜你喜欢
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 2011-11-03
    • 1970-01-01
    • 2014-05-11
    • 2010-09-21
    • 1970-01-01
    相关资源
    最近更新 更多