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

C# 与异步方法相关的VoidTaskResult类型是什么?,c#,dynamic,reflection,asynchronous,C#,Dynamic,Reflection,Asynchronous,最近我第一次使用async(实际上是.NET4.5),遇到了一些让我困惑的问题。我在网上找不到关于VoidTaskResult类的太多信息,所以我来这里是想看看是否有人知道发生了什么 我的代码如下所示。显然,这是非常简单的。基本思想是调用插件方法,这些方法是异步的。如果它们返回任务,则异步调用没有返回值。如果它们返回任务,则有。我们事先不知道它们是哪种类型,所以我们的想法是使用反射查看结果的类型(IsGenericType如果类型是type),并使用动态类型获取值 在我的真实代码中,我通过反射调

最近我第一次使用async(实际上是.NET4.5),遇到了一些让我困惑的问题。我在网上找不到关于
VoidTaskResult
类的太多信息,所以我来这里是想看看是否有人知道发生了什么

我的代码如下所示。显然,这是非常简单的。基本思想是调用插件方法,这些方法是异步的。如果它们返回
任务
,则异步调用没有返回值。如果它们返回
任务
,则有。我们事先不知道它们是哪种类型,所以我们的想法是使用反射查看结果的类型(
IsGenericType
如果类型是
type
),并使用动态类型获取值

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

// 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(如预期)

但是,如果您稍微更改plugin方法的声明,
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
?”,或者“当我动态调用异步方法时,为什么会发生这种奇怪的事情?”或者甚至可能是“在任何情况下,如何调用可选异步的插件方法?”,我把这一点说出来是希望其中一位大师能够有所启发。

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

首先,
Task
源于
Task
。我想你已经熟悉了

此外,您可以为执行代码的任务创建
任务
任务
类型。例如,如果第一个示例返回的是
Task.Run
或诸如此类的内容,那么这将返回一个实际的
Task
对象

<>这个问题是在考虑<代码> TaskCuleTyOrthOuts< /Cult>如何与任务层次结构交互时发生的。code>TaskCompletionSource
用于创建不执行代码的任务,而是作为某个操作已完成的通知。例如,超时、I/O包装器或
async
方法

没有非通用的
TaskCompletionSource
类型,因此,如果您希望这样的通知没有返回值(例如超时或
async Task
方法),则必须为某些
T
创建
TaskCompletionSource
,并返回
任务。
async
团队必须为
async Task
方法选择
T
,因此他们创建了类型
VoidTaskResult

通常这不是问题。由于
Task
源于
Task
,因此该值被转换为
Task
,并且每个人都很快乐(在静态世界中)。但是,由
TaskCompletionSource
创建的每个任务实际上都属于
task
类型,而不是
task
,您可以通过反射/动态代码看到这一点

最终的结果是,您必须像对待任务一样对待任务。然而,
VoidTaskResult
是一个实现细节;将来可能会改变

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

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

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

为什么异步团队引入了VoidTaskResult,而不仅仅是引入了一个非通用的TaskCompletionSource?@Puppy:我怀疑这是因为工作量要少得多。编写一个非通用的
TaskCompletionSource
并不难,但任何公共API在发布之前都必须经过一系列其他“关卡”。安全审查等。解释是有道理的。但是从实际的角度来看,您如何在运行时确定没有结果的任务和有结果的任务之间的区别呢?也就是说,由于
Task
甚至用于没有结果的任务,而且
VoidTaskResult
不是公共的,因此检查的唯一方法似乎是比较
Task
类型参数名称。我想这在技术上是可以的,但在这样的代码中,似乎必须将类型名指定为字符串。@PeterDuniho:这种运行时代码肯定是un-C。因为这是编译器永远不需要担心的事情;它总是使用声明的类型。我很想听听你的想法;也许有更好的解决方案,但不是我的方案本身。我看完后偶然发现了这个。我给出了一个答案,我认为这个答案解决了问题中的具体要求,但依赖非公共类型的文本名称的需要让我感到困扰(如果我可以使用
typeof
操作符,那就已经够糟糕了,但必须将名称作为文本进行比较对我来说尤其烦人)。