0

我正在调用一个我不拥有的方法,它返回一个“任务”对象。我想从我的方法中返回该对象,但我还需要覆盖该对象的 Exception 属性。如果底层任务捕获异常,并且我的用户通过 Exception 属性检索该异常,我需要调整他们在返回的异常中看到的数据。

我想知道这个问题是否有比通过反射进行复制更好的解决方案。我真的不想创建一个新实例,但我找不到避免它的方法。

另一种可能性是将 Task 对象包装在我自己的类中。但是我的方法不会返回一个任务(或派生自)对象,这可能会导致下游困难。更不用说实现所有 Task 的公共成员并且只调用基本版本的代码量。

显然,下面的代码不起作用,但从概念上讲,这是我想要做的:

public class MyTask : Task
{
    override Exception Exception { get { return Tweak(base.Exception); } }
}

public MyTask MyMethod()
{
    Task t = MethodIDontOwnThatReturnsTask();
    return (MyTask) t;     // Can I do this type of operation without copying t?
}
4

2 回答 2

3

不,一旦创建对象,您就不能更改它的类型。

我也不愿意通过反射执行副本。我很想创建一个新的TaskCompletionSource,并从返回的任务中添加一个延续,以TaskCompletionSource适当地设置结果,随时调整异常。

我已经有一些代码可以做到这一点,如果你实际上有一个Task<T>

var tcs = new TaskCompletionSource<SomeResultType>();
task.ContinueWith(completed =>
{
    PropagateResult(completed, tcs);
}, TaskContinuationOptions.ExecuteSynchronously);
return tcs.Task;
...

// Generally useful method...
private static void PropagateResult<T>(Task<T> completedTask,
    TaskCompletionSource<T> completionSource)
{
    switch (completedTask.Status)
    {
        case TaskStatus.Canceled:
            completionSource.TrySetCanceled();
            break;
        case TaskStatus.Faulted:
            // This is the bit you'd want to tweak
            completionSource.TrySetException(completedTask.Exception.InnerExceptions);
            break;
        case TaskStatus.RanToCompletion:
            completionSource.TrySetResult(completedTask.Result);
            break;
        default:
            throw new ArgumentException("Task was not completed");
    }
}

或者,如果您使用的是 C# 5,您甚至可以使用:

public async Task MyMethod()
{
    Task t = MethodIDontOwnThatReturnsTask();
    try
    {
        await t;
    }
    catch(Exception e)
    {
        throw Tweak(e);
    }
}

请注意,如果您需要抛出多个异常,这最终可能会带来更多问题——这取决于您的确切上下文。

于 2013-02-19T19:28:48.167 回答
2

与其试图改变现有的任务,或者改变它的显示方式,不如创建一个新的Task(这就是ContinueWith要做的)。它们并不昂贵:

public Task MyMethod()
{
    return MethodIDontOwnThatReturnsTask()
        .ContinueWith(t => { throw Tweak(t.Exception); }
        , TaskContinuationOptions.OnlyOnFaulted);
}
于 2013-02-19T19:28:58.980 回答