将TypeA的返回对象转换为从TypeA派生的类型

时间:2013-02-19 19:25:20

标签: c# casting upcasting

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

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

另一种可能性是将Task对象包装在我自己的类中。但是我的方法不会返回一个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?
}

2 个答案:

答案 0 :(得分: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);
    }
}

请注意,如果您需要抛出多个异常,最终可能会出现更多问题 - 这取决于您的具体情况。

答案 1 :(得分:2)

不是试图改变现有任务,也不改变其看法,只需创建一个新的TaskContinueWith将会做什么)。它们并不贵:

public Task MyMethod()
{
    return MethodIDontOwnThatReturnsTask()
        .ContinueWith(t => { throw Tweak(t.Exception); }
        , TaskContinuationOptions.OnlyOnFaulted);
}