异步方法中的 ref 和 out 参数

Posted

技术标签:

【中文标题】异步方法中的 ref 和 out 参数【英文标题】:ref and out arguments in async method 【发布时间】:2021-07-03 04:43:43 【问题描述】:

有谁知道为什么async 方法不允许有refout 参数?我对此进行了一些研究,但我唯一能找到的是它与堆栈展开有关。

【问题讨论】:

我想你可以在这里找到答案***.com/questions/18716928/… 看看来自social.msdn.microsoft.com/Forums/vstudio/en-US/…的servy42的回答 social.msdn.microsoft.com/Forums/en-US/… 相关:How to write an async method with out parameter? 【参考方案1】:

有谁知道为什么异步方法不允许有 ref 和 out 参数?

当然。想一想 - 一个异步方法通常返回 几乎立即,远在大多数实际逻辑执行之前......这是异步完成的。因此,任何out 参数都必须在第一个await 表达式之前分配,并且很可能必须对ref 参数进行一些限制,以阻止它们在第一个await 表达式之后使用,因为在那之后它们甚至可能无效。

考虑使用outref 参数调用异步方法,并使用局部变量作为参数:

int x;
int y = 10;
FooAsync(out x, ref y);

FooAsync 返回后,方法本身可以返回 - 因此这些局部变量在逻辑上将不再存在......但异步方法仍然可以有效地在其延续中使用它们。大问题。编译器可以创建一个新类来以与 lambda 表达式相同的方式捕获变量,但这会导致其他问题......当延续在不同的线程上运行时,通过方法的任意点。至少可以说很奇怪。

基本上,由于涉及时间问题,对async 方法使用outref 参数是没有意义的。请改用包含您感兴趣的所有数据的返回类型。

如果您只对在第一个 await 表达式之前更改的 outref 参数感兴趣,则始终可以将方法一分为二:

public Task<string> FooAsync(out int x, ref int y)

    // Assign a value to x here, maybe change y
    return FooAsyncImpl(x, y);


private async Task<string> FooAsyncImpl(int x, int y) // Not ref or out!


编辑:使用Task&lt;T&gt; 使用out 参数并像返回值一样直接在方法内分配值是可行的。不过这有点奇怪,而且它不适用于ref 参数。

【讨论】:

@hvd:是的,它可能会这样做 - 将编辑以将其包括在内。虽然它无法将一个out 参数作为另一个out 参数的参数传递... 当然,这很好。它可以传递传入的引用,但是如果 that 引用了未包装在类中的局部变量,您仍然会遇到同样的问题。 我想我弄错了,如果不扩展 CIL 是完全不可能的。 我不明白,你是说Action&lt;T&gt; 而不是Task&lt;T&gt;?如果是这样,好主意。 @hvd:不,我是说任务。将其视为另一个返回值 - 直到整个方法完成,任务才会完成,此时该值将是方法中分配的最后一个值。有点奇怪,但可行。【参考方案2】:

C#编译成CIL,CIL不支持。

CIL 本身没有asyncasync 方法被编译成一个类,所有(使用的)参数和局部变量都存储在类字段中,这样当调用该类的特定方法时,代码就知道在哪里继续执行以及变量有什么值.

refout参数使用托管指针实现,不允许托管指针类型的类字段,所以编译器无法保留传入的引用。

对类字段中托管指针的这种限制可以防止一些无意义的代码,正如 Jon Skeet 的回答中所解释的那样,因为类字段中的托管指针可能引用已经返回的函数的局部变量。然而,这个限制是如此严格,以至于即使是安全的和正确的使用也会被拒绝。 ref/out 字段可以工作,如果它们引用另一个类字段,并且编译器确保始终将使用 ref/out 传递的局部变量包装在一个类中(就像它已经知道该怎么做)。

因此,C# 根本无法解决 CIL 施加的限制。即使 C# 设计者想要允许它(我不是说他们允许),他们也不能。

【讨论】:

以上是关于异步方法中的 ref 和 out 参数的主要内容,如果未能解决你的问题,请参考以下文章

C#中关键字ref和out的区别

Ref和Out的区别

ref、val 和 out 对方法参数的含义是啥?

C#值参数和引用参数,方法的重载,foreach,数组,以及ref和out的用法

ref的使用

ref和out