2013-03-20 13 views
23

C#のタスクはクラスなので、Task<TDerived>を明らかにTask<TBase>にキャストできません。タスク<TDerived>をタスク<TBase>に変換する方法はありますか?

しかし、あなたが行うことができます:

public async Task<TBase> Run() { 
    return await MethodThatReturnsDerivedTask(); 
} 

は、私は基本的にただ根本的なタスクを指し、その結果をキャストTask<TDerived>インスタンスを取得するために呼び出すことができる静的なタスク方法はありますか?

public Task<TBase> Run() { 
    return Task.FromDerived(MethodThatReturnsDerivedTask()); 
} 

このような方法はありますか?この目的のためだけに非同期メソッドを使用するためのオーバーヘッドはありますか?

答えて

36

このような方法はありますか?

だけに、この目的のために非同期メソッドを使用する任意のオーバーヘッドはありますか?

はい。しかし、それは最も簡単な解決策です。

ThenのようなTaskの拡張メソッドであることに注意してください。スティーブン・トゥーはこれを探検しましたin a blog postと私は最近incorporated it into AsyncExです。 TaskCompletionSource<TBase>独自に作成することであろうわずかに少ないオーバーヘッドで

public Task<TBase> Run() 
{ 
    return MethodThatReturnsDerivedTask().Then(x => (TBase)x); 
} 

別のアプローチ、それは(私のAsyncExライブラリにTryCompleteFromCompletedTaskを使用して)導出結果で完了しました:Thenを使用して

、あなたのコードは次のようになります。

public Task<TBase> Run() 
{ 
    var tcs = new TaskCompletionSource<TBase>(); 
    MethodThatReturnsDerivedTask().ContinueWith(
     t => tcs.TryCompleteFromCompletedTask(t), 
     TaskContinuationOptions.ExecuteSynchronously); 
    return tcs.Task; 
} 

または(あなたがAsyncExへの依存性を取るしたくない場合):

public Task<TBase> Run() 
{ 
    var tcs = new TaskCompletionSource<TBase>(); 
    MethodThatReturnsDerivedTask().ContinueWith(t => 
    { 
    if (t.IsFaulted) 
     tcs.TrySetException(t.Exception.InnerExceptions); 
    else if (t.IsCanceled) 
     tcs.TrySetCanceled(); 
    else 
     tcs.TrySetResult(t.Result); 
    }, TaskContinuationOptions.ExecuteSynchronously); 
    return tcs.Task; 
} 
+0

を理由だけで、 '(MethodThatReturnsDerivedTaskを返さない)ContinueWith(タスク=>(TBASE)task.Result、TaskContinuationOptions。 .ExecuteSynchronously) '? –

+3

'Result'を使うと、例外は' AggregateException'にラップされ、元のタスクがキャンセルされた状態で完了した場合には完全なフォールトになります。つまり、これらの状況を正しく処理するコードをいくつか追加すれば、 'TaskCompletionSource'を使わずに' ContinueWith'を使って解決策を作ることができます。 –

15

このような方法はありますか?この目的のためだけに非同期メソッドを使用するためのオーバーヘッドはありますか?

これには組み込みの方法はなく、オーバーヘッドが発生します。

「最も軽い」代替手段は、TaskCompletionSource<T>を使用して、このための新しいタスクを作成することです。これは、そのような拡張メソッドを介して行うことができます:あなたはこの試みることができる

static Task<TBase> FromDerived<TBase, TDerived>(this Task<TDerived> task) where TDerived : TBase 
{ 
    var tcs = new TaskCompletionSource<TBase>(); 

    task.ContinueWith(t => tcs.SetResult(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion); 
    task.ContinueWith(t => tcs.SetException(t.Exception.InnerExceptions), TaskContinuationOptions.OnlyOnFaulted); 
    task.ContinueWith(t => tcs.SetCanceled(), TaskContinuationOptions.OnlyOnCanceled); 

    return tcs.Task; 
} 
+1

'SetException(t.Exception.InnerExceptions)'を実行して例外ブランチを処理し、 'AggregateException'ラッパーを回避することを検討してください。 –

+0

@StephenCleary良い提案 - 更新されました。 –

+0

あなたは 'TaskCompletionOptions'の代わりに' TaskContinuationOptions'を使用していますか? – Lukazoid

-1

task.ContinueWith<TDerived>(t => (TDerived)t.Result);

関連する問題