首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >无法将“Task<Derived>”转换为“Task<Interface>”

无法将“Task<Derived>”转换为“Task<Interface>”
EN

Stack Overflow用户
提问于 2016-06-14 17:35:01
回答 4查看 4.1K关注 0票数 23

下面的函数带有一个委托参数,该参数接受一个接口的类型,并返回另一个接口的任务。

代码语言:javascript
复制
public void Bar(Func<IMessage, Task<IResult>> func)
{
    throw new NotImplementedException();
}

我还有一个带有参数的函数,作为IMessage的一个实例,并返回一个任务。MessageResult分别是IMessageIResult的实现。

代码语言:javascript
复制
private Task<Result> DoSomething(Message m) { return new Task<Result>(() => new Result()); }

当我将DoSomething传递给Bar时,我会收到一个错误。

代码语言:javascript
复制
Bar(m => DoSomething((Message)m));
// Cannot convert type 'Task<Result>' to 'Task<IResult>'

为什么Result不隐式地转换为IResult

我可以想象这是一个协变性的问题。但是,在这种情况下,Result实现了IResult。我还试图通过创建接口并将TResult标记为协变量来解决协方差问题。

代码语言:javascript
复制
public interface IFoo<TMessage, out TResult>
{
    void Bar(Func<TMessage, Task<TResult>> func);
}

但我知道错误是:

无效方差:类型参数'TResult‘在IFoo<TMessage, TResult>.Bar(Func<TMessage, Task<TResult>>)上必须不变有效。'TResult‘是协变的。

现在我被困住了。我知道我有协方差的问题,但我不知道如何解决。有什么想法吗?

编辑:这个问题是特定于任务的。通过在我的应用程序中实现async await,我遇到了这个问题。我偶然发现了这个通用实现,并添加了一个Task。在这类转换过程中,其他人可能也会遇到同样的问题。

解决方案:以下是基于以下答案的解决方案:

代码语言:javascript
复制
Func<Task<Result>, Task<IResult>> convert = async m => await m;
Bar(m => convert(DoSomething((Message)m)));
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2016-06-14 17:52:41

C#不允许类上的差异,只允许使用引用类型参数化的接口和委托。Task<T>是一个类。

这有点不幸,因为Task<T>是可以安全地进行协变的罕见类之一。

但是,很容易将Task<Derived>转换为Task<Base>。只需创建一个接受Task<Derived>并返回Task<Base>的助手方法/ lambda,等待传入的任务,并将值转换为Base。C#编译器将处理其余部分。当然,你失去了引用的身份,但你永远不会得到一个类。

票数 32
EN

Stack Overflow用户

发布于 2016-06-14 18:07:33

似乎必须有一种更干净的方法来完成这个任务,但是创建正确类型的包装任务是可能的。我引入了一个名为GeneralizeTask()的新函数。

代码语言:javascript
复制
Task<TBase> GeneralizeTask<TBase, TDerived>(Task<TDerived> task) 
    where TDerived : TBase 
{
    var newTask = new Task<TBase>(() => {
        if (task.Status == TaskStatus.Created) task.Start();
        task.Wait();
        return (TBase)task.Result;
    });
    return newTask;
}

编辑:

正如@EricLippert所指出的,这可以大大简化。我最初试图找到一种实现此方法的方法,但没有找到编译的方法。事实证明,真正的解决办法比我想象的还要简单。

代码语言:javascript
复制
async Task<TBase> GeneralizeTask<TBase, TDerived>(Task<TDerived> task) 
    where TDerived : TBase 
{
    return (TBase) await task;
}

然后您可以像这样调用Bar()

代码语言:javascript
复制
Bar(m => GeneralizeTask<IResult, Result>(DoSomething((Message)m)));
票数 5
EN

Stack Overflow用户

发布于 2019-02-21 21:40:26

我正在使用另一个版本的GeneralizeTask,它是由@Recursive,在上声明的。下面是:

代码语言:javascript
复制
public static Task<TBase> GeneralizeTask<TDerived, TBase>(this Task<TDerived> task, CancellationToken cancellationToken = default) 
 where TBase : class 
 where TDerived : TBase
{
    var result = task.ContinueWith(t => (TBase)t.Result, cancellationToken);
    return result;
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37818642

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档