为什么成员函数调用未从调用函数中推断出所需的接收者?

时间:2019-09-10 04:59:37

标签: kotlin

考虑:

class Foo {
    fun CoroutineScope.foo() {

    }
}

class Bar {
    val f = Foo()

    fun CoroutineScope.bar() { // this could also be "suspend fun bar = coroutineScope {"
        f.foo() // unresolved reference

        with (f) { 
            foo() // OK
        }

        with (f) {
            with(this) {
                foo() // OK
            }
        }        
    }
}

似乎在f.foo()的首次尝试应该推断出CoroutineScope上指定的bar()接收者。似乎没有;但是为了更好地理解接收者,有人对为什么解释了吗?

修改

Playground link

查看了some docs(特别是“将扩展声明为成员” )和Rene的回复之后,我尝试了一些其他操作:

import kotlinx.coroutines.*

class Foo {
    fun CoroutineScope.foo() { println("Foo.foo")}
    fun Baz.fed(){ println("Foo.fed") }
}

class Baz {
    fun CoroutineScope.foo() { println("Baz.foo") }
    fun Foo.fed(){ println("Baz.fed") }
}

fun CoroutineScope.foo() { println("CoroutineScope.foo") }

fun foo() { println("Global.foo") }

fun bar(scope: CoroutineScope) {
    val f = Foo()
    val b = Baz()

    println ("Test 1")
    foo() // foo() from Global
    scope.foo() // foo() from CoroutineScope
    //f.foo() // unresolved reference

    println ("\nTest 2")
    with(scope) {
        foo() // foo() from CoroutineScope
        //f.foo() // unresolved reference
    }

    println ("\nTest 3")
    with(f) {
        scope.foo() // foo() from Foo
        foo() // foo() from Global
    }

    println ("\nTest 4")
    with(scope) {
        with (f) {
            foo() // foo() from Foo
            scope.foo() // foo() from Foo
        }
    }

    println ("\nTest 5")
    with(f) {
        with (scope) {
            foo() // foo() from Foo
            scope.foo() // foo() from Foo
        }
    }

    println ("\nTest 6")
    with(b) {
        with(f) {
            with (scope) {
                foo() // foo() from Foo

                fed() // fed() from Baz     
            }
        }
    }

    println ("\nTest 7")
    with(f) {
        with(b) {
            with (scope) {
                foo() // foo() from Baz

                fed() // fed() from Foo
            }
        }
    }
}

fun main() = runBlocking {
    bar(this)
}

有趣的是,当两个上下文都通过with可用时,它能够确定哪个是调度上下文,以及哪个扩展上下文,无论提供它们的顺序如何。但是,如果直接像f.bar()那样指定扩展上下文,它将使用扩展接收器查找bar的版本。 >类型FooFoo的直接成员(我仍然不清楚它如何查看 dispatch extension 接收者在类定义中简单定义的函数)。因此似乎逻辑是这样的:

给出表达式x.y()

  1. 找到所有带有扩展接收器的功能y() x
  2. 对于每个可用的接收器c,从最近添加的接收器开始,选择明确采用类型为调度接收器的第一个x.y() c-注意fun CoroutineScope.foo()在全局范围内的行为就像没有派发接收器,因为测试6和7表明即使将scope添加到可用的最后一个上下文列表,则使用Foo的{​​{1}}(或Baz)版本。

给出表达式foo()

  1. 尝试在可用上下文列表中找到同时定义了调度接收器扩展接收器(x)的y()。 (请注意:它首先选择最近添加的扩展接收器,然后尝试找到匹配的调度接收器(请参见测试6和7中的x.y()
  2. 如果#1没有返回任何内容,请在定义函数fed()
  3. 的可用上下文中,使用调度接收器 x选择最近添加的上下文。
  4. 什么都没有?使用定义功能y()
  5. 扩展接收器 x选择最近添加的上下文
  6. 退回到y()的全局版本

1 个答案:

答案 0 :(得分:3)

您的声明:

class Foo {
    fun CoroutineScope.foo() {

    }
}

在类foo的实例的上下文中定义CoroutineScope的扩展函数Foo。这意味着,如果您处于foo类型为CoroutineScope的范围内,则只能在this的实例上调用Foo

第一次尝试f.foo()的作用恰恰相反。您在foo()的实例上调用Foo,并且this引用了CoroutineScope

另外两个示例使用withthis的引用设置为Foo,因此您可以在外部foo实例上调用CoroutineScope

顺便说一句:with(this)毫无意义,因为this将设置为this