C#异步接口的同步实现
实际工程中有时需要将同步方法包装在异步方法之中。比如一个类实现了异步接口,而实际实现可能是本地计算的同步操作,也可能是需要HTTP请求的异步操作。对于前者,如果实现中没有await关键字,编译器会提示该方法缺少await运算符,将以同步方式运行。将二者统一的解决方案是使用Task.FromResult将结果包装成一个已完成的Task返回。举例:
首先定义了一个异步接口IWork,不失普遍性,它包括了有返回值和无返回值的两个方法:
public interface IWork { Task WorkAsync(); Task<int> WorkWithResultAsync(); }
既然定义为异步接口,那么实现此接口的AsyncClass如下,其中延时500ms为模拟的CPU或IO耗时异步操作:
public class AsyncClass : IWork { public async Task WorkAsync() { await Task.Delay(500); // Mock CPU intensive operations Console.WriteLine("RealAsync WorkAsync Done"); } public async Task<int> WorkWithResultAsync() { await Task.Delay(500); // Mock CPU intensive operations Console.WriteLine("RealAsync WorkWithResultAsync Done"); return 0; } }
但我们还需要有另一个实现此接口的类FakeAsyncClass,它仅需要简单的本地计算,并无await运算符:
此时编译器会在WorkAsync()和WorkWithResultAsync()两个方法下提示它们会以同步方式运行,这当然不是合理的做法。因此,我们使用Task.FromResult将其改进如下:public class FakeAsyncClass : IWork { public async Task WorkAsync() { Console.WriteLine("FakeAsync WorkAsync Done"); } public async Task<int> WorkWithResultAsync() { Console.WriteLine("FakeAsync WorkWithResultAsync Done"); return 0; } }
注意,除了使用Task.FromResult改变了返回值外,我们还将这两个方法的async关键字去掉了,即它们并非异步方法,仅返回一个Task或Taskpublic class FakeAsyncClass : IWork { public Task WorkAsync() { Console.WriteLine("FakeAsync WorkAsync Done"); return Task.CompletedTask; // Task.FromResult<object>(null) if < .NET 4.6 } public Task<int> WorkWithResultAsync() { Console.WriteLine("FakeAsync WorkWithResultAsync Done"); return Task.FromResult(0); } }
private static async Task SyncToAsync() { var workList = new List<IWork> { new AsyncClass(), new FakeAsyncClass() }; foreach (var work in workList) { await work.WorkAsync(); await work.WorkWithResultAsync(); } }
References
How to: Create Pre-Computed Tasks Task.FromResult(TResult) Method Task.CompletedTask Property
Preview: