避免在Task和Task<T>中出现重复的方法

6

我有一些关于TaskTask<T>的逻辑。 有没有办法避免重复的代码?

目前我的代码如下:

public async Task<SocialNetworkUserInfo> GetMe()
{
    return await WrapException(() => new SocialNetworkUserInfo());
}

public async Task AuthenticateAsync()
{
    await WrapException(() => _facebook.Authenticate());
}

public async Task<T> WrapException<T>(Func<Task<T>> task)
{
    try
    {
        return await task();
    }
    catch (FacebookNoInternetException ex)
    {
        throw new NoResponseException(ex.Message, ex, true);
    }
    catch (FacebookException ex)
    {
        throw new SocialNetworkException("Social network call failed", ex);
    }
}

public async Task WrapException(Func<Task> task)
{
    try
    {
        await task();
    }
    catch (FacebookNoInternetException ex)
    {
        throw new NoResponseException(ex.Message, ex, true);
    }
    catch (FacebookException ex)
    {
        throw new SocialNetworkException("Social network call failed", ex);
    }
}
2个回答

3
你可以使Task的超载调用另一个函数,并返回虚拟值。
public async Task WrapException(Func<Task> task)
{
    await WrapException<object>(async () => {
        await task();
        return null;
    });
}

或者,因为这里不需要使用async关键字:

public Task WrapException(Func<Task> task)
{
    return WrapException<object>(async () => {
        await task();
        return null;
    });
}

1
虽然这是可能的,但加入的开销以及与“Task”相关的代码通常是性能敏感代码的事实意味着,虽然这是可能的,但在实际生产代码中通常不是一个好主意。 - Servy
@Servy 奇怪的是,我本以为网络访问的IO延迟会使这个简单的“async”lambda的开销微不足道。但如果你发现这里有显著的时间浪费,你可以(最坏的情况下)像原帖一样复制代码。如果你有更好的方法,我(和Grigory,我相信)很乐意听取建议。 - Tim S.
1
@TimS。这就是它的全部,使用任务有几个目的。它们在网络IO等方面的用途,在这种情况下开销不是问题,还有它们在CPU密集型多线程代码中的使用,其中它将是一个明显的开销。由于大多数通用任务相关方法不知道它们将使用哪种类型的任务,因此它们需要被设计为能够有效地处理任何一种任务。我不知道有什么特别好的方法来解决这个问题,除了复制。 - Servy
@Servy 好的,说得好!不过,这个特定的方法正在捕获“Facebook..Exception”,因此很明显它是用于网络IO相关任务的。 - Tim S.
@TimS。是的,这就是为什么我在这里没有问题;评论更多地是针对那些可能考虑将这种技术应用于自己情况(可能不涉及Facebook)的读者。 - Servy

1
假设Func本身不会抛出异常,以下代码将起作用。
public async Task<T> WrapException<T>(Func<Task<T>> task)
{
    var actualTask = task();
    await WrapException((Task)actualTask);
    return actualTask.Result;
}

我们知道,由于WrapException确保其运行完成,因此Result不会阻塞或抛出异常。

感谢您的快速回答!这段代码令人遗憾的是,在实际异步操作开始之前抛出的任何异常都将在await之前抛出。并且异常不会传递到实际的WrapException方法中。 - Grigory

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