todas - try catch c#
¿Cuál es la mejor forma de detectar excepciones en Tarea? (2)
Con System.Threading.Tasks.Task<TResult>
, tengo que administrar las excepciones que podrían lanzarse. Estoy buscando la mejor manera de hacerlo. Hasta ahora, he creado una clase base que gestiona todas las excepciones no detectadas dentro de la llamada de .ContinueWith(...)
Me pregunto si hay una mejor forma de hacerlo. O incluso si es una buena forma de hacerlo.
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
*/
});
}
}
Hay dos formas de hacerlo, dependiendo de la versión del idioma que esté utilizando.
C # 5.0 y superior
Puede utilizar la función async
y await
palabras clave para simplificar una gran cantidad de esto para usted.
async
y await
se introdujeron en el lenguaje para simplificar el uso de la Biblioteca de tareas paralelas , lo que evita que tenga que usar ContinueWith
y le permite continuar programando de forma descendente.
Debido a esto, puedes simplemente usar un bloque try/catch para atrapar la excepción, así:
try
{
// Start the task.
var task = Task.Factory.StartNew<StateObject>(() => { /* action */ });
// Await the task.
await task;
}
catch (Exception e)
{
// Perform cleanup here.
}
Tenga en cuenta que el método que encapsula lo anterior debe usar tener la palabra clave async
aplicada para que pueda usar await
.
C # 4.0 y abajo
Puede manejar excepciones utilizando la sobrecarga ContinueWith
que toma un valor de la enumeración TaskContinuationOptions
, así:
// Get the task.
var task = Task.Factory.StartNew<StateObject>(() => { /* action */ });
// For error handling.
task.ContinueWith(t => { /* error handling */ }, context,
TaskContinuationOptions.OnlyOnFaulted);
El miembro OnlyOnFaulted
de la enumeración TaskContinuationOptions
indica que la continuación solo debe ejecutarse si la tarea antecedente arrojó una excepción.
Por supuesto, puede tener más de una llamada para ContinueWith
con el mismo antecedente, manejando el caso no excepcional:
// 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();
Puede crear una Fábrica de Tareas personalizada, que producirá Tareas con procesamiento de manejo de excepciones incrustado. Algo como esto:
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
);
}
};
Puede olvidarse del procesamiento de excepciones para Tareas producidas desde esta fábrica en su código de cliente. Al mismo tiempo, puede esperar el final de tales tareas o usarlas en el estilo de "Olvidar y olvidar":
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
Pero si soy sincero, no estoy muy seguro de por qué quieres tener un código de manejo de finalización. En cualquier caso, esta decisión depende de la lógica de su aplicación.