1. ホーム
  2. c#

[解決済み] タスクで例外をキャッチする最適な方法は何ですか?

2023-05-01 09:34:03

質問

System.Threading.Tasks.Task<TResult> で、私は投げられる可能性のある例外を管理しなければなりません。そのための最良の方法を探しています。今のところ、私はキャッチされない例外をすべて .ContinueWith(...)

もっと良い方法はないかと考えています。あるいは、それが良い方法であるかどうかさえも。

public class BaseClass
{
    protected void ExecuteIfTaskIsNotFaulted<T>(Task<T> e, Action action)
    {
        if (!e.IsFaulted) { action(); }
        else
        {
            Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
            {
                /* I display a window explaining the error in the GUI 
                 * and I log the error.
                 */
                this.Handle.Error(e.Exception);
            }));            
        }
    }
}   

public class ChildClass : BaseClass
{
    public void DoItInAThread()
    {
        var context = TaskScheduler.FromCurrentSynchronizationContext();
        Task.Factory.StartNew<StateObject>(() => this.Action())
                    .ContinueWith(e => this.ContinuedAction(e), context);
    }

    private void ContinuedAction(Task<StateObject> e)
    {
        this.ExecuteIfTaskIsNotFaulted(e, () =>
        {
            /* The action to execute 
             * I do stuff with e.Result
             */

        });        
    }
}

どのように解決するのですか?

使用している言語のバージョンによって、2つの方法があります。

C# 5.0 およびそれ以上

を使用することができます。 async await というキーワードを使うことで、かなりの部分を簡略化することができます。

asyncawait の使用を簡略化するために、言語に導入されました。 タスク並列ライブラリ を使う必要がないようにするためです。 ContinueWith を使う必要がなくなり、トップダウン方式でプログラミングを続けることができるようになります。

このため、単純に try / catch ブロックを使って、このような例外をキャッチします。

try
{
    // Start the task.
    var task = Task.Factory.StartNew<StateObject>(() => { /* action */ });

    // Await the task.
    await task;
}
catch (Exception e)
{
    // Perform cleanup here.
}

上記のメソッドをカプセル化した を使用するには async キーワードが適用されるので await .

C# 4.0 およびそれ以下

例外を処理するには ContinueWith オーバーロード から値を受け取る TaskContinuationOptions 列挙 というように

// Get the task.
var task = Task.Factory.StartNew<StateObject>(() => { /* action */ });

// For error handling.
task.ContinueWith(t => { /* error handling */ }, context,
    TaskContinuationOptions.OnlyOnFaulted);

OnlyOnFaulted のメンバは TaskContinuationOptions の列挙は、継続が だけ が実行されることを示します。

もちろん、2つ以上の ContinueWith を呼び出すことができます。

// Get the task.
var task = new Task<StateObject>(() => { /* action */ });

// For error handling.
task.ContinueWith(t => { /* error handling */ }, context, 
    TaskContinuationOptions.OnlyOnFaulted);

// If it succeeded.
task.ContinueWith(t => { /* on success */ }, context,
    TaskContinuationOptions.OnlyOnRanToCompletion);

// Run task.
task.Start();