2012-10-19 23 views
52

Con System.Threading.Tasks.Task<TResult>, devo gestire le eccezioni che potrebbero essere generate. Sto cercando il modo migliore per farlo. Finora, ho creato una classe base che gestisce tutte le eccezioni non rilevate all'interno della chiamata di .ContinueWith(...)Qual è il modo migliore per rilevare l'eccezione nell'attività?

Mi chiedo se c'è un modo migliore di farlo. O anche se è un buon modo per farlo.

risposta

68

Ci sono due modi per farlo, in base alla versione della lingua che si sta utilizzando.

C# 5.0 e superiori

È possibile utilizzare i async e await parole chiave per semplificare una grande quantità di questo per voi.

async e await sono stati introdotti nella lingua di semplificare utilizzando il Task Parallel Library, impedisce di dover utilizzare ContinueWith e che consente di continuare a programmare in modo top-down.

A causa di questo, si può semplicemente utilizzare un blocco try/catch per intercettare l'eccezione, in questo modo:

try 
{ 
    // Start the task. 
    var task = Task.Factory.StartNew<StateObject>(() => { /* action */ }); 

    // Await the task. 
    await task; 
} 
catch (Exception e) 
{ 
    // Perform cleanup here. 
} 

Si noti che il metodo di incapsulare quanto sopra mosto uso hanno la parola async applicata in modo da poter utilizzare await.

C# 4.0 e di seguito

È possibile gestire le eccezioni utilizzando il ContinueWith overload che prende un valore dal TaskContinuationOptions enumeration, in questo modo:

// Get the task. 
var task = Task.Factory.StartNew<StateObject>(() => { /* action */ }); 

// For error handling. 
task.ContinueWith(t => { /* error handling */ }, context, 
    TaskContinuationOptions.OnlyOnFaulted); 

La OnlyOnFaulted membro del TaskContinuationOptions enumerazione indica che la continuazione dovrebbe solo essere eseguito se l'attività antecedente ha generato un'eccezione.

Naturalmente, è possibile avere più di una chiamata alla ContinueWith fuori lo stesso antecedente, si occupano del caso non eccezionale:

// 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(); 
+0

Come faresti a conoscere il tipo di eccezione nel metodo anonimo? Se faccio t.Exception, intellisense non esporrà la proprietà di eccitazione, messaggio ... ecc ... – guiomie

+4

@guiomie 't' * è * l'eccezione. – casperOne

+1

contesto non è definito di cosa si tratta? – MonsterMMORPG

2

È possibile creare una fabbrica Task personalizzati, che produrrà attività con la gestione delle eccezioni elaborazione incorporata. Qualcosa del genere:

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 
     ); 
    } 
}; 

È possibile dimenticare l'elaborazione delle eccezioni per le attività prodotte da questo stabilimento nel codice cliente. Nello stesso tempo si può ancora aspettare finitura di tali compiti o utilizzarli in fire-and-Forget stile:

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 

Ma se essere onesti non sono davvero sicuro perché si vuole avere il codice di gestione di completamento. In ogni caso questa decisione dipende dalla logica della tua applicazione.

Problemi correlati