Avec System.Threading.Tasks.Task<TResult>
, Je dois gérer les exceptions qui pourraient être levées. Je cherche le meilleur moyen de le faire. Jusqu'ici, j'ai créé une classe de base qui gère toutes les exceptions non capturées dans l'appel de .ContinueWith(...)
Je me demande s'il existe un meilleur moyen de le faire. Ou même si c'est un bon moyen de le faire.
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
*/
});
}
}
Vous pouvez le faire de deux manières, en fonction de la version de la langue que vous utilisez.
Vous pouvez utiliser les mots clés async
et await
pour vous simplifier la tâche.
async
et await
ont été introduits dans le langage pour simplifier l'utilisation de Task Parallel Library , vous évitant ainsi de devoir utiliser ContinueWith
et vous permettant de continuer à programmer de manière descendante.
Pour cette raison, vous pouvez simplement utiliser un bloc try
/catch
== pour capturer l'exception, comme suit:
try
{
// Start the task.
var task = Task.Factory.StartNew<StateObject>(() => { /* action */ });
// Await the task.
await task;
}
catch (Exception e)
{
// Perform cleanup here.
}
Notez que la méthode encapsulant ce qui précède doit utiliser le mot-clé async
afin que vous puissiez utiliser await
.
Vous pouvez gérer les exceptions en utilisant la surcharge ContinueWith
_ qui prend une valeur de l'énumération TaskContinuationOptions
, comme suit:
// Get the task.
var task = Task.Factory.StartNew<StateObject>(() => { /* action */ });
// For error handling.
task.ContinueWith(t => { /* error handling */ }, context,
TaskContinuationOptions.OnlyOnFaulted);
Le membre OnlyOnFaulted
de l'énumération TaskContinuationOptions
indique que la poursuite doit uniquement être exécutée si la tâche antécédente a généré une exception. .
Bien entendu, vous pouvez avoir plusieurs appels à ContinueWith
du même antécédent, en gérant le cas non exceptionnel:
// 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();
Vous pouvez créer une fabrique de tâches personnalisée qui produira des tâches avec le traitement de la gestion des exceptions incorporé. Quelque chose comme ça:
using System;
using System.Threading.Tasks;
class FaFTaskFactory
{
public static Task StartNew(Action action)
{
return Task.Factory.StartNew(action).ContinueWith(
c =>
{
AggregateException exception = c.Exception;
// Your Exception Handling Code
},
TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously
).ContinueWith(
c =>
{
// Your task accomplishing Code
},
TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously
);
}
public static Task StartNew(Action action, Action<Task> exception_handler, Action<Task> completion_handler)
{
return Task.Factory.StartNew(action).ContinueWith(
exception_handler,
TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously
).ContinueWith(
completion_handler,
TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously
);
}
};
Vous pouvez oublier le traitement des exceptions pour les tâches générées par cette fabrique dans votre code client. Dans le même temps, vous pouvez toujours attendre la fin de ces tâches ou les utiliser dans le style Fire-and-Forget:
var task1 = FaFTaskFactory.StartNew( () => { throw new NullReferenceException(); } );
var task2 = FaFTaskFactory.StartNew( () => { throw new NullReferenceException(); },
c => { Console.WriteLine("Exception!"); },
c => { Console.WriteLine("Success!" ); } );
task1.Wait(); // You can omit this
task2.Wait(); // You can omit this
Mais si je suis honnête, je ne sais pas vraiment pourquoi vous voulez avoir un code de traitement de complétion. Dans tous les cas, cette décision dépend de la logique de votre application.