我想知道如何编写可链接的异步扩展方法,而不需要调用者编写多个等待和嵌套括号。
实施例。假设您的目标是让调用者能够编写这种代码段:
var example = new MyCompilableClass();
await example.Compile().Run();
(注意:我不是在编写编译器。我只是使用这些名称来说明一个必须先发生在另一个之前)。
为了支持上述内容,您需要创建两个接口:
public interface ICompilable
{
Task<IRunnable> CreateExecutableImage();
}
public interface IRunnable
{
Task Execute();
}
您将它们实现为异步:
class SourceCode : ICompilable
{
public async Task<IRunnable> CreateExecutableImage()
{
await Stub.DoSomethingAsynchronous();
return new ObjectCode();
}
}
class ObjectCode : IRunnable
{
public async Task Execute()
{
await Stub.DoSomethingAsynchronous();
}
}
然后使用适当的类型约束编写两个扩展方法:
static class ExtensionMethods
{
public static async Task<IRunnable> Compile<T>(this T This) where T : ICompilable
{
return await This.CreateExecutableImage();
}
public static async Task Run<T>(this T This) where T : IRunnable
{
await This.Execute();
}
}
所以现在调用者尝试编译他的代码。但是我们在这一行上得到了一个错误:
await example.Compile().Run(); //Does not compile
以下是编译错误:
类型'System.Threading.Tasks.Task'不能在泛型类型或方法'ExtensionMethods.Run(T)'中用作类型参数'T'。没有从'System.Threading.Tasks.Task'到'Example.IRunnable'的隐式引用转换
我们可以用括号修复编译错误:
(await example.Compile()).Run();
......或两行代码:
var compiled = await example.Compile();
await compiled.Run();
......两者都有效。但是,如果您期待与LINQ一样的干净,可链接的语法,那似乎相当不幸。
是否有不同的方法来实现这些扩展方法,以便它们保持异步性质,但不需要丑陋的语法?
如果你想使用我的示例代码,这是一个Link to DotNetFiddle。
答案 0 :(得分:1)
一个简单的答案就是添加另一种将Task<T>
转换为T
的扩展方法,如下所示:
static class ExtensionMethods
{
public static async Task Run<T>(this T This) where T : IRunnable
{
await This.Execute();
}
public static async Task Run<T>(this Task<T> This) where T : IRunnable
{
////Await the task and pass it through to the original method
await (await This).Execute();
}
}
这将使调用者能够使用
await example.Compile().Run();
......虽然他可能不知道他将任务而不是结果传递给Run()
(除非他真的考虑过它)。对他来说无关紧要。