与异步方法有关的VoidTaskResult类型是什么?

时间:2013-07-24 03:47:10

标签: c# dynamic reflection asynchronous

我最近第一次使用异步(和.Net 4.5),我遇到过让我难过的东西。关于我可以在网上找到的VoidTaskResult课程的信息不多,所以我来到这里是为了看看是否有人对这是怎么回事。

我的代码如下所示。显然,这很简单。基本思想是调用异步的插件方法。如果它们返回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;):

RuntimeBinderException

如果您深入了解任务对象,它实际上似乎是Type。 VoidTaskResult是线程名称空间中的私有类型,几乎没有任何内容。

VoidTaskResult task

我尝试更改我的调用代码:

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?”,或者“为什么当我动态调用异步方法时会发生这种奇怪的事情?”甚至可能“如何调用可选的异步插件方法?”在任何情况下,我都把它放在那里,希望其中一位大师能够解决这些问题。

1 个答案:

答案 0 :(得分:13)

这是由于设计任务(特别是任务完成源)的类层次结构的方式。

首先,Task<T>来自Task。我假设你已经熟悉了。

此外,您可以为执行代码的任务创建TaskTask<T>类型。例如,如果您的第一个示例正在返回Task.Run或诸如此类的东西,那么这将返回一个实际的Task对象。

当您考虑TaskCompletionSource<T>如何与任务层次结构进行交互时,会出现此问题。 TaskCompletionSource<T>用于创建不执行代码的任务,而是充当某些操作已完成的通知。例如,超时,I / O包装或async方法。

没有非通用TaskCompletionSource类型,因此如果您想要这样的通知而没有返回值(例如,超时或async Task方法),那么您必须创建一个{{ 1}}对于某些TaskCompletionSource<T>并返回TTask<T>小组必须为async方法选择T,因此他们创建了async Task类型。

通常这不是问题。由于VoidTaskResult派生自Task<T>,因此该值会转换为Task,并且每个人都很满意(在静态世界中)。但是,Task创建的每个任务实际上都是TaskCompletionSource<T>类型,而不是Task<T>,您可以通过反射/动态代码看到这一点。

最终结果是,您必须像对待Task一样对待Task<VoidTaskResult>。但是,Task是一个实现细节;它可能会在未来发生变化。

因此,我建议您实际将逻辑建立在VoidTaskResult的(声明的)返回类型上,而不是(实际)返回值。这更接近于模仿编译器的作用。

yada