Kotlin的crossinline和noinline有什么不同?

  1. 此代码编译时会有一个警告 (对性能影响不明显 ):

    inline fun test(noinline f: () -> Unit) { thread(block = f) } 
  2. 此代码不能编译非法使用内联参数 ):

     inline fun test(crossinline f: () -> Unit) { thread(block = f) } 
  3. 此代码编译时会有一个警告 (对性能影响不明显 ):

     inline fun test(noinline f: () -> Unit) { thread { f() } } 
  4. 此代码编译没有警告或错误

     inline fun test(crossinline f: () -> Unit) { thread { f() } } 

这是我的问题:

  • (2)如何不编译,但(4)呢?
  • noinlinecrossinline什么crossinline
  • 如果(3)不产生一个没有性能改进,为什么会(4)呢?

来自内联函数的参考 :

请注意,某些内联函数可能会将传递给它们的lambda作为参数,而不是直接从函数体中调用,而是从另一个执行上下文(如本地对象或嵌套函数)中调用。 在这种情况下,lambda中也不允许非本地控制流量。 为了表明,lambda参数需要用crossinline修饰符标记

因此,示例2不会编译,因为crossinline仅执行本地控制流,而表达式block = f违反了这一点。 例1编译,因为noinline不需要这样的行为(显然,因为它是一个普通的函数参数)。

示例1和3不会产生任何性能改进,因为唯一的lambda参数被标记为noinline ,使该函数的inline修饰符无用且冗余 – 编译器想要内联某些东西,但是所有可能被标记为不是内联。

考虑两个功能, AB.

一个

 inline fun test(noinline f: () -> Unit) { thread { f() } } 

 fun test(f: () -> Unit) { thread { f() } } 

函数A的行为就像函数B一样,参数f不会内联( B函数不内联test体,而在A函数中,body: thread { f() }仍然被内联)。

现在,在例4中这是不正确的,因为crossinline f: () -> Unit参数可以内联,所以它不能违反上述非局部控制流规则(就像将新值赋给全局变量一样)。 如果可以内联,编译器会假设性能得到改进,并不像示例3那样发出警告。

Q1:怎么(2)不编译,但是(4)呢?

从他们的文档:

Inlinable lambda只能在内联函数中调用或作为inlinable参数传递…

回答:

thread(...)方法thread(...)不是inline方法,所以你不能将f作为参数传递。

Q2:noinline和crossinline的区别是什么?

回答:

noinline将阻止lambda的内联。 当你有多个lambda参数,并且你只想把一些lambda表达式传递给一个内联函数的时候,这会变得很有用。

crossinline用于标记不允许非本地返回的lambdas,特别是当这样的lambda传递给另一个执行上下文时。 换句话说,你将无法在这样的lambda表达式中使用return 。 用你的例子:

 inline fun test(crossinline f: () -> Unit) { thread { f() } } //another method in the class fun foo() { test{ //Error! return is not allowed here. return } } 

问题3:如果(3)没有产生一个没有性能改进,为什么会(4)呢?

回答:

那是因为你在(3)中唯一的lambda已经被标记为noinline ,这意味着你将有创建Function对象来容纳你的lamda的开销。 对于(4)lambda仍然内联(性能改进)只是它不会允许非本地回报。

第一个和第二个问题

(2)如何不编译,但是(4)呢? noinlinecrossinline之间的crossinline

 2. inline fun test(crossinline f: () -> Unit) { thread(block = f) } 4. inline fun test(crossinline f: () -> Unit) { thread { f() } } 

这两种情况都有inline修饰符指示内联函数test及其参数lambda f 。 从kotlin参考:

内联修饰符影响函数本身和传递给它的lambda表达式:所有这些将被内联到调用站点中。

所以编译器被指示放置代码(内联),而不是为f构造和调用一个函数对象。 crossinline修饰符仅用于内联事物:它只是说传递的lambda(在f参数中)不应该有非本地返回(这是“正常”内联lambda可能有)。 crossinline可以被认为是这样的(对编译器的指令):“做内联,但有一个限制,它跨越了调用者上下文,所以确保lambda没有非本地返回。

在一个侧面说明, thread似乎是一个概念上说明crossinline thread例子,因为显然从一个不同的线程返回一些代码(在f传递)不能影响从test的返回,它继续在调用者线程上执行,催生( f继续独立执行)

在情况#4,有一个lambda(花括号)调用f() 。 在情况#2中, f直接作为参数传递给thread

所以在#4中,调用f()可以内联,编译器可以保证没有非本地返回。 详细来说,编译器会用它的定义代替f() ,然后代码被封装在封装的lambda中,换句话说, { //code for f() }是另外一种(包装器)lambda,它本身进一步作为函数对象引用传递给( thread )。

在#2的情况下,编译器错误只是说它不能内联f因为它被作为参考传递到“未知”(非内联)的地方。 crossinline变得crossinline ,在这种情况下是不相关的,因为只有f被内联才能适用。

总结起来,情况2和情况4与kotlin参考的例子不同(参见“高阶函数和Lambdas”):下面的调用是等价的,其中大括号(lambda表达式)“替换”包装器函数被toBeSynchronized

 //want to pass `sharedResource.operation()` to lock body fun <T> lock(lock: Lock, body: () -> T): T {...} //pass a function fun toBeSynchronized() = sharedResource.operation() val result = lock(lock, ::toBeSynchronized) //or pass a lambda expression val result = lock(lock, { sharedResource.operation() }) 

在问题中的情况#2和#4是不相同的,因为在#2中没有调用f “包装器”