考虑以下C#代码
var L1 =
Task.Run(() =>
{
return Task.Run(() =>
{
return Task.Run(() =>
{
return Task.Run(() =>
{
return new Dummy();
});
});
});
});
var L2 =
Task.Run(async () =>
{
return await Task.Run(async () =>
{
return await Task.Run(async () =>
{
return await Task.Run(async () =>
{
return new Dummy();
});
});
});
});
var L3 =
Task.Run(async () =>
{
return Task.Run(async () =>
{
return Task.Run(async () =>
{
return Task.Run(async () =>
{
return new Dummy();
});
});
});
});
var r1 = L1.Result;
var r2 = L2.Result;
var r3 = L3.Result;
======================================================================
乍一看,L1、L2和L3看起来都像
Task<Task<Task<Task<Dummy>>>>
事实证明,L1和L2仅仅是Task<Dummy>
所以,我查找Task.Run的MSDN
(我的重载Task.Run是:Task.Run<TResult>(Func<Task<TResult>>)
)
MSDN说:
返回值是:一个任务(TResult),它表示函数返回的任务(TResult)的代理。
它还在评论中说:
语言编译器使用
Run<TResult>(Func<Task<TResult>>)
方法来支持异步和等待关键字。它不打算直接从用户代码中调用。
所以,看起来我不应该在代码中使用这个重载的Task.Run,
如果我这样做了,编译器将去掉额外的Task<Task<Task<...<TResult>>>>
层,只需给您一个Task<TResult>
。
如果您鼠标移动到L1和L2上,它会告诉您这是Task<Dummy>
,而不是我的
预期Task<Task<Task<Task<Dummy>>>>
到目前为止还不错,直到我看到L3
L3看起来与L1和L2几乎完全一样。区别是:
L3只包含async
关键字,而不是await
,与L1和L2不同,L1和L2都没有,L2也没有。
令人惊讶的是,L3现在被认为是Task<Task<Task<Task<Dummy>>>>
,不像L1和L2,两者都被认为是Task<Dummy>
。
我的问题:
是什么导致编译器将L3与L1和L2区别对待。为什么简单地将'async'
添加到L1 (或将await
从L2中删除)会导致编译器对它有不同的对待?
如果将更多的Task.Run级联到L1/L2/L3,Visual就会崩溃。我使用的是VS2013,如果我级联5层或更多层的Task.Run,它就会崩溃。4层是我能得到的最好的,这就是为什么我使用4层作为我的例子。就我一个人吗?在翻译Task.Run时编译器会发生什么?
谢谢
发布于 2015-02-06 02:41:31
是什么导致编译器将L3与L1和L2区别对待。为什么简单地将‘异步’添加到L1 (或从L2中移除等待)会导致编译器以不同的方式对待它?
因为async
和await
更改了lambda表达式中的类型。您可以认为async
是“添加”Task<>
包装器,而await
是“移除”Task<>
包装器。
只需考虑与最内部呼叫有关的类型。首先,L1:
return Task.Run(() =>
{
return new Dummy();
});
() => { return new Dummy(); }
的类型是Func<Dummy>
,所以Task.Run
的返回类型是Task<Dummy>
。
因此,() => ###Task<Dummy>###
的类型是Func<Task<Dummy>>
,它调用Task.Run
,返回类型为Task<Dummy>
。诸若此类。
现在考虑一下L2:
return await Task.Run(async () =>
{
return new Dummy();
});
async () => { return new Dummy(); }
的类型是Func<Task<Dummy>>
,所以Task.Run
的返回类型是Task<Dummy>
。
async () => await ###Task<Dummy>###
的类型是Func<Task<Dummy>>
,因此它使用结果类型Task<Dummy>
调用Task.Run
。诸若此类。
最后,L3:
return Task.Run(async () =>
{
return new Dummy();
});
async () => { return new Dummy(); }
的类型还是Func<Task<Dummy>>
,所以Task.Run
的返回类型是Task<Dummy>
。
async () => { return ###Task<Dummy>### }
的类型是Func<Task<Task<Dummy>>>
。注意嵌套的任务。因此,再次调用Task.Run
,但它的返回类型是Task<Task<Dummy>>
。
现在,你只需重复每一层。async () => { return ###Task<Task<Dummy>>### }
的类型是Func<Task<Task<Task<Dummy>>>>
。Task.Run
再次被调用,但它的返回类型是Task<Task<Task<Dummy>>>
。诸若此类。
如果将更多的Task.Run级联到L1/L2/L3,Visual就会崩溃。我使用的是VS2013,如果我级联5层或更多层的Task.Run,它就会崩溃。4层是我能得到的最好的,这就是为什么我使用4层作为我的例子。就我一个人吗?在翻译Task.Run时编译器会发生什么?
谁在乎?任何现实世界的代码都不会这么做。有一些众所周知的场景,编译器很难在合理的时间框架内处理;在方法重载解析中使用lambda表达式是一种。使用lambda表达式使编译器更难成倍地工作。嵌套调用。
https://stackoverflow.com/questions/28357489
复制相似问题