次のコードをご覧ください。
static void Main(string[] args)
{
// Get the task.
var task = Task.Factory.StartNew<int>(() => { return div(32, 0); });
// For error handling.
task.ContinueWith(t => { Console.WriteLine(t.Exception.Message); },
TaskContinuationOptions.OnlyOnFaulted);
// If it succeeded.
task.ContinueWith(t => { Console.WriteLine(t.Result); },
TaskContinuationOptions.OnlyOnRanToCompletion);
Console.ReadKey();
Console.WriteLine("Hello");
}
private static int div(int x, int y)
{
if (y == 0)
{
throw new ArgumentException("y");
}
return x / y;
}
リリースモードでコードを実行すると、出力は「1つ以上のエラーが発生しました」となり、「Enterキー」を押すと「Hello」も表示されます。デバッグモードでコードを実行すると、出力はリリースモードと同じです。しかし、IDEでデバッグする場合、コントロールが行を実行するとIDE例外メッセージ(「ユーザーコードで未処理の例外」)が表示されます。
throw new ArgumentException("y");
そこから続行すると、プログラムはクラッシュせず、リリース モードと同じ出力が表示されます。これは例外を処理する適切な方法でしょうか?
ベストアンサー1
OnlyOnFaulted
おそらく、個別のおよびハンドラーは必要なくOnlyOnRanToCompletion
、 を処理していませんOnlyOnCanceled
。チェックこの答え詳細については。
しかし、IDEでデバッグする場合、コントロールが次の行を実行すると、IDE例外メッセージ(「ユーザーコードで未処理の例外」)が表示されます。
おそらくデバッグ/例外オプション ( Ctrl+ Alt+ E) で例外を有効にしているため、デバッガーの下に例外が表示されます。
そこから続行すると、プログラムはクラッシュせず、リリース モードと同じ出力が表示されます。これは例外を処理する適切な方法でしょうか?
アクション内でスローされたが処理されなかった例外は、Task
自動的に再スローされません。代わりに、将来の観察のためにTask.Exception
(型AggregateException
) としてラップされます。元の例外には次のようにアクセスできますException.InnerException
。
Exception ex = task.Exception;
if (ex != null && ex.InnerException != null)
ex = ex.InnerException;
この場合、プログラムをクラッシュさせるには、実際に例外を観察する必要があります。外タスクアクション、例えば以下を参照するTask.Result
:
static void Main(string[] args)
{
// Get the task.
var task = Task.Factory.StartNew<int>(() => { return div(32, 0); });
// For error handling.
task.ContinueWith(t => { Console.WriteLine(t.Exception.Message); },
TaskContinuationOptions.OnlyOnFaulted);
// If it succeeded.
task.ContinueWith(t => { Console.WriteLine(t.Result); },
TaskContinuationOptions.OnlyOnRanToCompletion);
Console.ReadKey();
Console.WriteLine("result: " + task.Result); // will crash here
// you can also check task.Exception
Console.WriteLine("Hello");
}
詳細:タスクと未処理の例外、.NET 4.5 でのタスク例外処理。
コメントに対応するために更新しました:.NET 4.0 と VS2010 を使用した UI アプリでこれを行う方法は次のとおりです。
void Button_Click(object sender, EventArgs e)
{
Task.Factory.StartNew<int>(() =>
{
return div(32, 0);
}).ContinueWith((t) =>
{
if (t.IsFaulted)
{
// faulted with exception
Exception ex = t.Exception;
while (ex is AggregateException && ex.InnerException != null)
ex = ex.InnerException;
MessageBox.Show("Error: " + ex.Message);
}
else if (t.IsCanceled)
{
// this should not happen
// as you don't pass a CancellationToken into your task
MessageBox.Show("Canclled.");
}
else
{
// completed successfully
MessageBox.Show("Result: " + t.Result);
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
.NET 4.0をターゲットにし、監視されていない例外に対して.NET 4.0の動作(つまり、タスクがガベージコレクションされたときに再スローする)が必要な場合は、明示的に以下で設定しますapp.config
:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
<runtime>
<ThrowUnobservedTaskExceptions enabled="true"/>
</runtime>
</configuration>
詳細については、こちらをご覧ください: