c# - 与异步方法相关的 VoidTaskResult 类型是什么?

标签 c# dynamic reflection asynchronous

我最近第一次使用异步(实际上是 .Net 4.5),我遇到了一些让我难过的事情。关于 VoidTaskResult 的信息不多我可以在网上找到的类(class),所以我来这里看看是否有人对正在发生的事情有任何想法。

我的代码如下所示。显然,这简化了很多。基本思想是调用异步的插件方法。如果他们返回 Task ,则异步调用没有返回值。如果他们返回 Task<> ,那么有。我们事先不知道它们是哪种类型,所以我们的想法是使用反射查看结果的类型(如果类型是 IsGenericTypeType<> 为真)并使用动态类型获取值。

在我的真实代码中,我通过反射调用插件方法。我认为这不会对我所看到的行为产生影响。

// 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是错误的(如预期的那样)。

但是,如果您稍微更改插件方法的声明,IsGenericType现在返回 true 并且内容中断:

public async Task yada()
{
 // stuff
}

执行此操作时,将在 object result = dynTask.Result; 行抛出以下异常:

RuntimeBinderException

如果深入研究任务对象,它实际上看起来是 Type<VoidTaskResult> . VoidTaskResult是 Threading namespace 中的私有(private)类型,其中几乎没有任何内容。

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

最佳答案

这是由于围绕任务(尤其是任务完成源)的类层次结构的设计方式所致。

首先,Task<T>源自 Task .我想您已经很熟悉了。

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

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

没有非通用的 TaskCompletionSource类型,所以如果你想要没有返回值的通知(例如,超时或 async Task 方法),那么你必须创建一个 TaskCompletionSource<T>对于一些 T并返回 Task<T> . async团队必须选择 T对于 async Task方法,因此他们创建了类型 VoidTaskResult .

通常这不是问题。自 Task<T>源自 Task , 该值将转换为 Task每个人都很开心(在静态世界中)。但是,TaskCompletionSource<T> 创建的每个任务实际上是 Task<T> 类型, 不是 Task ,您会通过反射/动态代码看到这一点。

最终的结果是你必须对待Task<VoidTaskResult>就像它是Task .然而,VoidTaskResult是一个实现细节;将来可能会改变。

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

Task task = (Task)yadaMethod.Invoke(...);
await task;

if (yadaMethod.ReturnType.IsGenericType)
{
  ...
}

关于c# - 与异步方法相关的 VoidTaskResult 类型是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17824863/

相关文章:

Javafx 网格 Pane ..无法动态添加行..而不收缩

reflection - 为什么 reflect.ValueOf 不能在 golang 中使用 Println 打印出 int 的真实值

ruby - 是否有可能获得 Ruby 中的所有特征类?

c# - 编辑并继续功能在 Visual Studio 2010 中停止工作

html - Rails Mailer 自定义动态内容

php - 创建动态 PHP 数组

c# - C#中的每个对象是如何实现GetType的?

c# - 如何根据 Combobox 的值更改 DataGridView 单元格颜色?

c# - 神圣的倒影

c# - 是否可以使用简写条件运算符抛出异常? C#