如何将一个Task转换为Task<T>?

3

我相信这个问题以前已经被问过,但我好像无法用正确的措辞得到任何解决我的问题的答案。

我有一个基类,它实现了一个带有返回Task的Save方法的接口:

public interface ICanSave
{
    Task Save();
}

public class Base : ICanSave
{
    public Task Save()
    {
        return Task.CompletedTask;
    }
}

当然,Save方法实际上会执行一些操作,比如保存到数据库中,这就是为什么它是一个Task,因此可以等待。如果我有一个继承自基类的Foo类:
public class Foo : Base { }

目前,您需要分两步执行实例化和保存:

var foo = new Foo();

await foo.Save();

然而,我希望能够通过表达式进行实例化和保存,并返回一个任务。我想出了这个助手:

public static class Helper<T> where T : ICanSave
{
    public static Task<T> Save(T obj)
    {
        var source = new TaskCompletionSource<T>();

        ThreadPool.QueueUserWorkItem(_ =>
        {
            obj.Save();
            source.SetResult(obj);
        });

        return source.Task;
    }
}

这可以用来做什么:

var foo = await Helper<Foo>.Save(new Foo());

我希望能够实现的结果更接近于这个,但是看起来相当繁琐,而且助手似乎会将保存到任务两次(一次是原始任务,另一次是助手中)。

是否有更简单的方法将任务转换为T类型的任务?


顺便提一下,检查一下 Task.Run,在这些情况下比 ThreadPool.QueueUserWorkItem 更合适。 - CSharpie
我可能错了,但根据我所读的内容,Task.Run在底层使用ThreadPool.QueueUserWorkItem。 - Aaron
是的,但它提供了一个通用版本,可以解决您的初始问题。 如何将一个 Task 转换为 Task<T>? - CSharpie
1个回答

6
我认为这种帮助方法没有意义,但如果你真的想要,可以像这样实现:

public static class Helper<T> where T : ICanSave {
    public static async Task<T> Save(T obj) {
        await obj.Save();
        return obj;
    }
}

感谢Evk,我最初选择了@Enigmativity的答案,因为a)他成功地在一行中完成了所有操作,这符合我正在尝试开发的函数式编程风格;b)因为他的方法返回了一个Task of T而不是async Task of T。但是,在我的项目中测试了您的解决方案后,它同样有效,并且考虑到您关于异常丢失的评论,我接受了您的答案。 - Aaron
@Aaron,"async Task" 这种东西是不存在的。async 关键字会让编译器以一定的方式重写你的方法,但它仍然返回 Task<T>,就像任何没有 async 关键字的方法一样。此外,强制在没有明显原因的情况下一行内完成事情不会带来任何好处(也不必要导致“函数式风格”)。 - Evk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接