【问题标题】:Get result of a non generic task获取非通用任务的结果
【发布时间】:2014-05-24 06:07:08
【问题描述】:

Task<T> 被向下转换为非通用Task 时,有没有办法获得它的结果? 我知道这可能与反射有关,但有没有办法做到这一点?也许通过使用Expression? 这是我想做的一个例子:

RunResult RunTask(Task task)
{
    task.Wait();

    object result = ???;

    if (result is RunResult)
        return (RunResult)result;

    return RunResult.Success;
}

我知道我可以通过使用不同的重载来实现这一点,但我不喜欢行为会根据选择的重载而改变。

如果任务没有结果,那么在这种情况下 null 就可以了。

【问题讨论】:

  • 听起来您最好将任务转换为Task<RunResult>
  • @Asad 我同意,这将是一个很好的方法,但不幸的是,这些任务已经作为基本类型通过系统。
  • RunResult 只是一个枚举,但它可能要复杂得多。

标签: c# .net


【解决方案1】:

这似乎是Task<RunResult>,对吧?然后将其转换为该类型并提取结果。

如果我的这个猜测是错误的,您可以使用dynamic 快速获取Result 属性的值。

((dynamic)myTask).Result

现在这个结果值的类型是dynamic,所以我不确定你想如何从中提取数据。

【讨论】:

  • 这会起作用(结果可以分配给一个对象,如果它是一个可以被捕获的非通用Task,它会抛出)。不幸的是,这涉及到 DLR,它完全是另外一头野兽。
  • 你知道真正的泛型类型参数是什么吗?然后只需转换为该类型。
  • 这可以正常工作,因为您在对数据运行转换之前安全地测试了 is。使用它的好地方!
  • @usr 不,我没有,为了解决这个问题,我们必须检查整个类层次结构(对于 RunResult 来说,这只是 RunResult 和对象 - 但可能还有更多)。
  • 我进行了一些测试,动态比反射快是正确的(在我的测试中大约快 30%)。一个专门的表达式仍然可以将它们从水中吹出来,并且比使用动态快约 400%(但需要一些我的时间没有考虑的同步)。直接访问比表达式方法快约 700%。如果有人感兴趣,我可以在我原来的问题中发布代码。
【解决方案2】:

如果你传入一个Task<T> 实例,其中Result 是从RunResult 派生的东西,你会得到这个值作为这个函数的返回值。否则,你会得到null

RunResult RunTask(Task task)
{
    task.Wait();
    var type = task.GetType();
    if (!type.IsGenericType)
    {
        return null;
    }
    //Could also use: ((dynamic)task).Result as RunResult
    return type.GetProperty("Result").GetValue(task) as RunResult;
}

【讨论】:

  • 是的,你会这么想,但考虑一下我们有一个 Task<object> 可以返回一个 RunResult 的情况。
  • 我希望除了反射之外还有其他方法可以做到这一点。
  • @Janiels 没有,抱歉。如果您之前丢弃了编译时类型,则需要使用反射。
  • 难道不能使用Expression 并制作一棵提取结果的树吗?然后根据Task的类型缓存它?我很确定这是执行此操作的最高效方式,但我只是想知道是否有可能以更......“静态”的方式进行
  • @Janiels 这样做没有意义。当您处于反射设计的情况下,为什么您如此反对使用反射?
【解决方案3】:

Task 不公开“结果”属性,并且由于Task 不是逆变的(即<T> 不是<out T>),您不能简单地转换为Task<object>

除反射之外的另一种方法是让通用方法处理您的代码。由于方法可以是泛型,因此您可以将其传递给任务。这有一个额外的优势,即您可以在不显式设置泛型参数的情况下调用该方法,因为它们可以从编译器的使用情况中推断出来。

RunResult RunTask<T>(Task<T> task)
{
    task.Wait();

    T result = task.Result;

    if (result is RunResult)
        return (RunResult)result;

    return RunResult.Success;
}

【讨论】:

  • 只有接口和委托可以有反/协变类型参数,所以甚至不可能使 T 协变。但是这种方法的问题与 Timothy Shields 的回答相同;我们仍然需要检查Task&lt;object&gt;Task&lt;RunStatus&gt;;可能还有许多其他变体。
  • 我真的不明白你的问题。您想从非通用任务中提取结果,如果它是 RunResult,则交付它,但您不想检查并将结果转换为 RunResult。除此之外,您拒绝使用反射和动态类型。所以告诉我们你正确答案的标准是什么,而不是仅仅拒绝所有提供的可能性。
  • 我的问题是是否可以不使用反射(正如我的问题所说的那样)。动态方式是一种很好的方式,但不幸的是加载了 DLR,在这种情况下我宁愿回退到反射。无法将Task 转换为Task&lt;RunResult&gt;,因为它可能是Task&lt;object&gt;,但仍返回RunResult。如果是更复杂的类型,Task&lt;T&gt; 会有很多不同的变体需要检查。
  • 一开始你为什么不喜欢反射解决方案?
  • 反射方式很好(尽管反射 API 很慢,但在我的情况下并不重要)。问题是我希望有更好、更“静态”的方法。
猜你喜欢
  • 2017-02-20
  • 1970-01-01
  • 2012-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-27
相关资源
最近更新 更多