And*_*Gis 19 .net c# asynchronous
我一直在玩async/await,发现了一些有趣的东西.看看下面的例子:
// 1) ok - obvious
public Task<IEnumerable<DoctorDto>> GetAll()
{
IEnumerable<DoctorDto> doctors = new List<DoctorDto>
{
new DoctorDto()
};
return Task.FromResult(doctors);
}
// 2) ok - obvious
public async Task<IEnumerable<DoctorDto>> GetAll()
{
IEnumerable<DoctorDto> doctors = new List<DoctorDto>
{
new DoctorDto()
};
return await Task.FromResult(doctors);
}
// 3) ok - not so obvious
public async Task<IEnumerable<DoctorDto>> GetAll()
{
List<DoctorDto> doctors = new List<DoctorDto>
{
new DoctorDto()
};
return await Task.FromResult(doctors);
}
// 4) !! failed to build !!
public Task<IEnumerable<DoctorDto>> GetAll()
{
List<DoctorDto> doctors = new List<DoctorDto>
{
new DoctorDto()
};
return Task.FromResult(doctors);
}
Run Code Online (Sandbox Code Playgroud)
考虑案例3和4.唯一的区别是3使用async/await关键字.3构建正常,但是4给出了关于将List隐式转换为IEnumerable的错误:
Cannot implicitly convert type
'System.Threading.Tasks.Task<System.Collections.Generic.List<EstomedRegistration.Business.ApiCommunication.DoctorDto>>' to
'System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<EstomedRegistration.Business.ApiCommunication.DoctorDto>>'
Run Code Online (Sandbox Code Playgroud)
什么是async/await关键字在这里改变了?
avo*_*avo 25
Task<T>
根本不是协变型.
虽然List<T>
可以转换为IEnumerable<T>
,但Task<List<T>>
转换为Task<IEnumerable<T>>
.在#4中,Task.FromResult(doctors)
返回Task<List<DoctorDto>>
.
在#3中,我们有:
return await Task.FromResult(doctors)
Run Code Online (Sandbox Code Playgroud)
这与以下相同:
return await Task<List<DoctorDto>>.FromResult(doctors)
Run Code Online (Sandbox Code Playgroud)
这与以下相同:
List<DoctorDto> result = await Task<List<DoctorDto>>.FromResult(doctors);
return result;
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为List<DoctorDto>
可以转换IEnumerable<DoctorDto>
.
Ste*_*ary 14
想想你的类型.Task<T>
不是变体,所以它不可转换Task<U>
,即使T : U
.
但是,如果t
是Task<T>
,那么类型await t
是T
,并且T
可以转换为U
if T : U
.
很明显,您明白为什么List<T>
至少可以返回为IEnumerable<T>
: 仅仅因为它实现了该接口。
同样很明显,第三个示例正在做一些“额外”的事情,而第四个示例则没有。正如其他人所说,第 4 次失败是因为缺乏协方差(或者相反,我永远不记得他们走了哪条路!),因为您直接试图提供一个实例 Task<List<DoctorDto>>
作为Task<IEnumerable<DoctorDto>>
.
第三遍的原因是因为await
添加了大量“支持代码”以使其按预期工作。这段代码解析Task<T>
成T
,这样return await Task<something>
将返回泛型中关闭的类型Task<T>
,在这种情况下something
。
方法签名然后返回Task<T>
并且它再次工作由编译器解决,它需要Task<T>
, Task
, 或void
对于异步方法,并简单地将您的T
背部按摩为 aTask<T>
作为所有背景生成的异步/等待延续gubbins的一部分。
正是这个额外的步骤,T
从await
并需要将它转换回一个Task<T>
给它工作所需的空间。您不是试图采用 a 的现有实例Task<U>
来满足 a Task<T>
,而是创建一个全新的Task<T>
,给它 a U : T
,并且在构造时,隐式强制转换如您所料发生(与您期望IEnumerable<T> myVar = new List<T>();
的工作方式完全相同) .
责备/感谢编译器,我经常这样做;-)