2012-01-05 26 views
9

Tengo un método que toma un argumento de devolución de llamada para ejecutar de forma asíncrona, pero el bloque catch no parece captar ninguna excepción lanzada por la llamada síncrona (this.Submit se refiere a un método síncrono) .Capturando una excepción lanzada en una devolución de llamada asíncrona

public void Submit(FileInfo file, AnswerHandler callback) 
{ 
    SubmitFileDelegate submitDelegate = new SubmitFileDelegate(this.Submit); 
    submitDelegate.BeginInvoke(file, (IAsyncResult ar) => 
    { 
     string result = submitDelegate.EndInvoke(ar); 
     callback(result); 
    }, null); 
} 

¿Hay alguna manera de detectar la excepción lanzada por el nuevo hilo y enviarlo al hilo original? Además, ¿es esta la forma "correcta" de manejar excepciones asíncronas? Escribí mi código por lo que se podría llamar así (asumiendo que el problema excepción es fija):

try 
{ 
    target.Submit(file, (response) => 
    { 
     // do stuff 
    }); 
} 
catch (Exception ex) 
{ 
    // catch stuff 
} 

pero hay una manera más adecuada o elegante de hacer esto?

+0

El bloque de catch en su primer ejemplo de código capturará una excepción lanzada por callback o por EndInvoke.El bloque catch en su segundo ejemplo de código detectará cualquier excepción lanzada por el constructor SubmitFileDelegate o por BeginInvoke. ¿Cuál no está haciendo lo que espera/quiere que haga? – dgvid

+0

Vaya, se olvidó de eliminar eso. Quiero que el segundo funcione correctamente, pero por el momento, tampoco lo está. – kevmo314

+0

¿Puede mostrar cómo se define 'SubmitFileDelegate'? – GolfWolf

Respuesta

8

Esto no es una solución 'mejores prácticas', pero yo creo que es muy simple que debería funcionar.

En lugar de tener el delegado define como

private delegate string SubmitFileDelegate(FileInfo file); 

definirlo como

private delegate SubmitFileResult SubmitFileDelegate(FileInfo file); 

y definir el SubmitFileResult como sigue:

public class SubmitFileResult 
{ 
    public string Result; 
    public Exception Exception; 
} 

A continuación, el método que realmente hace la la presentación del archivo (no se muestra en la pregunta) debe definirse así:

private static SubmitFileResult Submit(FileInfo file) 
{ 
    try 
    { 
     var submissionResult = ComplexSubmitFileMethod(); 

     return new SubmitFileResult { Result = submissionResult }; 
    } 
    catch (Exception ex) 
    { 
     return new SubmitFileResult {Exception = ex, Result = "ERROR"}; 
    } 
} 

De esta manera, examinará el objeto resultante, verá si tiene el campo Resultado o Excepción establecido, y actuará en consecuencia.

+0

¿No es esta llamada sincrónica? Intenté poner try/catch en la devolución de llamada de BeginInvoke (y seguir con su ejemplo) pero no pareció captar la excepción. – kevmo314

+0

No, no es sincrónico. Tenga en cuenta que hay dos métodos 'Enviar': el que describí anteriormente (que tiene que coincidir con la definición 'SubmitFileDelegate') y el que usted presentó en la pregunta, que' BeginInvoke's el otro (lo que significa que es una llamada asíncrona) – GolfWolf

3

En resumen, no.

Cuando llama al submitDelegate.BeginInvoke, genera el nuevo hilo, regresa y sale rápidamente de su bloque try/catch (mientras el nuevo hilo se ejecuta en segundo plano).

Se podría, sin embargo, la captura todas las excepciones no controladas como esto:

AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(YourException);

Esto cogerá todo en el dominio de aplicación, sin embargo (no sólo su método asíncrono).

+0

¿Hay una forma preferida de manejar excepciones entonces? Estoy acostumbrado a que las devoluciones de llamadas sean algo así como function (err, result) {} del mundo JS, pero no he visto nada parecido en las muestras del código C# que he revisado, así que no estoy seguro de que así sea se supone que debe hacerse en C# ...: S – kevmo314

+0

La respuesta de @ w0lf es básicamente eso; envuelve la excepción que ocurre dentro de su tipo de respuesta y la saca después de que se completa. –

9

Si se dirige a .NET 4.0, puede utilizar la nueva Biblioteca de tareas paralelas y observar la propiedad Exception del objeto Task.

public Task Submit(FileInfo file) 
{ 
    return Task.Factory.StartNew(() => DoSomething(file)); 
} 

private void DoSomething(FileInfo file) 
{ 
    throw new Exception(); 
} 

Entonces utilizar de esta manera:

Submit(myFileInfo).ContinueWith(task => 
{ 
    // Check task.Exception for any exceptions. 

    // Do stuff with task.Result 
}); 

donde DoSomething es el método desea llamada asíncrona, y el delegado se pasa a ContinueWith es su devolución de llamada.

Más información sobre el manejo de excepciones en el TPL se puede encontrar aquí: http://msdn.microsoft.com/en-us/library/dd997415.aspx

Cuestiones relacionadas