有谁知道为什么async
不允许方法ref
和out
参数?我已经做了一些或研究它,但我唯一能找到的是它与堆栈展开有关.
Jon*_*eet 96
有谁知道为什么不允许异步方法有ref和out参数?
当然.想一想 - 异步方法通常几乎立即返回,早在大多数实际逻辑被执行之前......这是异步完成的.因此out
,必须在第一个await
表达式之前分配任何参数,并且可能必须对ref
参数进行一些限制以阻止它们在第一个await
表达式之后被使用,因为之后它们甚至可能无效.
考虑调用异步方法与out
和ref
参数,采用局部变量的参数:
int x;
int y = 10;
FooAsync(out x, ref y);
Run Code Online (Sandbox Code Playgroud)
后FooAsync
返回时,方法本身可能返回-所以这些局部变量将不再存在逻辑......但异步方法仍然有效能够在其延续使用它们.大问题.编译器可以创建一个新类来捕获变量,就像它对lambda表达式一样,但这会导致其他问题......除了其他任何问题,你可以通过一个方法在任意点改变一个局部变量,当continuation在不同的线程上运行时.奇怪的是至少可以说.
基本上,由于涉及的时间安排,使用方法out
和ref
参数是没有意义的async
.使用包含您感兴趣的所有数据的返回类型.
如果你只在兴趣out
和ref
第一前参数改变await
的表达,你总是可以一分为二的方法:
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!
{
}
Run Code Online (Sandbox Code Playgroud)
编辑:out
使用参数Task<T>
并直接在方法中分配值就像返回值一样是可行的.虽然这有点奇怪,但它不适用于ref
参数.
小智 14
C#编译为CIL,CIL不支持此功能.
CIL本身没有async
.async
方法被编译为一个类,所有(使用过的)参数和局部变量都存储在类字段中,因此当调用该类的特定方法时,代码知道继续执行的位置以及变量具有的值.
ref
和out
参数是使用托管指针实现的,并且不允许托管指针类型的类字段,因此编译器无法保留传入的引用.
对类字段中托管指针的这种限制可以防止一些无意义的代码,如Jon Skeet的答案中所解释的那样,因为类字段中的托管指针可能引用已返回的函数的局部变量.但是,这种限制非常严格,即使安全和正确使用也会被拒绝.该ref
/ out
场可以工作,如果他们提到了另一个类领域,并确保编译器总是缠上通过局部变量ref
/ out
在一个类(像它已经知道该怎么做).
所以,C#根本无法解决CIL施加的限制.即使C#设计师想要允许它(我不是说他们这样做),他们也不能.