次のコードをご覧ください
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");
そこから続行すると、プログラムはクラッシュせず、リリースモードと同じ出力が表示されます。これは例外を処理する適切な方法ですか?
おそらく、別個のOnlyOnFaulted
ハンドラーとOnlyOnRanToCompletion
ハンドラーは必要なく、OnlyOnCanceled
を処理していません。詳細については this answer を確認してください。
ただし、IDEでデバッグする場合、コントロールが行を実行すると、IDE例外メッセージ(「ユーザーコードの未処理の例外」)が表示されます
おそらくデバッガー/例外オプションで有効にしているため、デバッガーの下に例外が表示されます(Ctrl+Alt+E)。
そこから続行すると、プログラムはクラッシュせず、リリースモードと同じ出力が表示されます。これは例外を処理する適切な方法ですか?
スローされたがTask
アクション内で処理されなかった例外は、自動的に再スローされません。代わりに、将来の観測のためにTask.Exception
(AggregateException
型)としてラップされます。元の例外にはException.InnerException
としてアクセスできます:
Exception ex = task.Exception;
if (ex != null && ex.InnerException != null)
ex = ex.InnerException;
この場合にプログラムをクラッシュさせるには、タスクアクションの例外outsideを実際に観察する必要があります。 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の動作が必要な限り、explicitlyapp.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>
詳細については、これを確認してください。
あなたが持っているものはAggregateException
です。これはタスクからスローされ、特定の例外を見つけるために内部例外を確認する必要があります。このような:
task.ContinueWith(t =>
{
if (t.Exception is AggregateException) // is it an AggregateException?
{
var ae = t.Exception as AggregateException;
foreach (var e in ae.InnerExceptions) // loop them and print their messages
{
Console.WriteLine(e.Message); // output is "y" .. because that's what you threw
}
}
},
TaskContinuationOptions.OnlyOnFaulted);
.Net 4.5以降、AggregateException.GetBaseException()
を使用して「この例外の根本原因」を返すことができます。
しかし、ドキュメントは少しずれているようです。別のAggregateExceptionを返すと主張します。ただし、スローされたArgumentExceptionを返すことがわかると思います。
「1つまたは複数のエラーが発生しました」は、タスクプールによって作成されたラッパー例外に由来します。必要な場合は、Console.WriteLine(t.Exception.ToString())
を使用して例外全体を出力します。
IDEは、処理されたかどうかに関係なく、すべての例外を自動的にキャプチャします。
タスクを使用しているため、実行中に発生したすべての例外をラップする AggregateException を取得する必要があります。 AggregateException.ToString()
メソッドのデフォルト出力であるため、One or more errors occurred
メッセージが表示されます。
例外のインスタンスの Handle メソッドが必要です。
また、このような例外を処理する適切なアプローチ here も参照してください。