S'il vous plaît jeter un oeil sur le code suivant-
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;
}
Si j'exécute le code en mode validation, le résultat est "Une ou plusieurs erreurs sont survenues" et une fois que j'ai appuyé sur la touche "Entrée", "Bonjour" s'affiche également. Si j'exécute le code en mode débogage, le résultat est identique à mode de publication. Mais lors du débogage dans l'EDI, un IDE ("exception non gérée dans le code utilisateur")) apparaît lorsque le contrôle exécute la ligne
throw new ArgumentException("y");
Si je continue à partir de là, le programme ne plante pas et affiche le même résultat que le mode de publication. Est-ce la bonne façon de gérer les exceptions?
Vous n'avez probablement pas besoin de gestionnaires séparés OnlyOnFaulted
et OnlyOnRanToCompletion
, et vous ne gérez pas OnlyOnCanceled
. Vérifiez cette réponse pour plus de détails.
Mais lors du débogage dans l'EDI, un message d'erreur IDE ("exception non gérée dans le code utilisateur")) lorsque le contrôle exécute la ligne
Vous voyez l’exception sous le débogueur parce que vous l’avez probablement activée dans les options Debug/Exceptions (Ctrl+Alt+E).
Si je continue à partir de là, le programme ne plante pas et affiche le même résultat que le mode de publication. Est-ce la bonne façon de gérer les exceptions?
Une exception levée mais non gérée dans une action Task
ne sera pas automatiquement ré-levée. Au lieu de cela, il sera emballé pour l'observation future en tant que Task.Exception
(de type AggregateException
). Vous pouvez accéder à l'exception d'origine en tant que Exception.InnerException
:
Exception ex = task.Exception;
if (ex != null && ex.InnerException != null)
ex = ex.InnerException;
Pour que le programme se bloque dans ce cas, vous devez en réalité observer l'exception extérieur l'action, par exemple. en référençant le 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");
}
Plus de détails: Tâches et exceptions non gérées , Gestion des exceptions de tâches dans .NET 4.5.
Mis à jour pour répondre au commentaire: , voici comment je procéderais dans une application d'interface utilisateur avec .NET 4.0 et VS2010:
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());
}
Tant que vous ciblez .NET 4.0 et que vous souhaitez que le comportement .NET 4.0 soit appliqué aux exceptions non observées (par exemple, une nouvelle tentative lorsque la tâche est lue), vous devez explicitement configurez-le dans le 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>
Vérifiez ceci pour plus de détails:
Ce que vous avez là est un AggregateException
. Cela provient des tâches et vous oblige à vérifier les exceptions internes pour en trouver des spécifiques. Comme ça:
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);
A partir de .Net 4.5, vous pouvez utiliser AggregateException.GetBaseException()
pour renvoyer "la cause première de cette exception".
La documentation semble être un peu en retrait cependant. Il prétend renvoyer une autre exception AggregateException. Cependant, je pense que vous constaterez qu'il renvoie l'erreur ArgumentException qui a été levée.
"Une ou plusieurs erreurs survenues" provient d'une exception d'encapsuleur créée par le pool de tâches. Utilisez Console.WriteLine(t.Exception.ToString())
pour imprimer toute l’exception si vous en avez besoin.
Les IDE peuvent capturer automatiquement toutes les exceptions, qu’elles aient été gérées ou non.
Puisque vous utilisez des tâches, vous devriez obtenir AggregateException , qui englobe toutes les exceptions survenues au cours de l'exécution. Vous voyez le message One or more errors occurred
, Parce que c'est la sortie par défaut de la méthode AggregateException.ToString()
.
Vous avez besoin de la méthode Handle de l'instance de l'exception.
Voir également la bonne approche pour gérer de telles exceptions here .