我的代码如下所示.显然,这很简单.基本思想是调用异步的插件方法.如果它们返回Task,则异步调用没有返回值.如果他们返回任务<>,那么就有.我们事先不知道它们是哪种类型,因此我们的想法是使用反射来查看结果的类型(如果类型为Type<>,则IsGenericType为true)并使用动态类型获取值.
在我的真实代码中,我通过反射调用插件方法.我认为这不应该对我所看到的行为产生影响.
// plugin method public Task yada() { // stuff } public async void doYada() { Task task = yada(); await task; if (task.GetType().IsGenericType) { dynamic dynTask = task; object result = dynTask.Result; // do something with result } }
这适用于上面显示的插件方法. IsGenericType为false(正如预期的那样).
但是,如果稍微更改插件方法的声明,IsGenericType现在返回true并且东西中断:
public async Task yada() { // stuff }
执行此操作时,行上会抛出以下异常(object result = dynTask.Result;):
如果你深入研究任务对象,它实际上似乎是类型. VoidTaskResult是线程名称空间中的私有类型,几乎没有任何内容.
public async void doYada() { Task task = yada(); await task; if (task.GetType().IsGenericType) { object result = task.GetType().GetProperty("Result").GetMethod.Invoke(task,new object[] { }); // do something with result } }
这种“成功”意味着它不再抛出,但现在结果是“VoidTaskResult”类型,我无法明智地做任何事情.
我应该补充一点,即使为这一切制定一个真实的问题,我也很难.也许我真正的问题是“什么是VoidTaskResult?”,或者“为什么当我动态调用异步方法时会发生这种奇怪的事情?”甚至可能“如何调用可选的异步插件方法?”在任何情况下,我都把它放在那里,希望其中一位大师能够解决这些问题.
解决方法
首先,任务< T>派生自任务.我假设你已经熟悉了.
此外,您可以创建任务或任务的类型< T>用于执行代码的任务.例如,如果你的第一个例子是返回Task.Run或whatnot,那么那将返回一个实际的Task对象.
当您考虑TaskCompletionSource< T>的方式时会出现问题.与任务层次结构交互. TaskCompletionSource< T>用于创建不执行代码的任务,而是充当某些操作已完成的通知.例如,超时,I / O包装或异步方法.
没有非通用的TaskCompletionSource类型,因此如果您希望这样的通知没有返回值(例如,超时或异步任务方法),那么您必须创建一个TaskCompletionSource< T>对于某些T并返回任务< T>.异步团队必须为异步任务方法选择一个T,因此他们创建了类型VoidTaskResult.
通常这不是问题.由于任务< T>派生自Task,将值转换为Task,每个人都很开心(在静态世界中).但是,TaskCompletionSource< T>创建的每个任务都是由TaskCompletionSource创建的.实际上是任务< T>类型,而不是任务,你看到这与反射/动态代码.
最终结果是你必须处理Task< VoidTaskResult>就像是Task.但是,VoidTaskResult是一个实现细节;它可能会在未来发生变化.
因此,我建议您实际将逻辑基于yada的(声明的)返回类型,而不是(实际)返回值.这更接近于模仿编译器的功能.
Task task = (Task)yadaMethod.Invoke(...); await task; if (yadaMethod.ReturnType.IsGenericType) { ... }