2010-01-20 15 views
12

este código produce "valor de salida".¿Cómo obtener un valor a través de un parámetro out/ref de un método que arroja una excepción?

class P 
{ 
    public static void Main() 
    { 
    string arg = null; 
    try 
    { 
     Method(out arg); 
    } 
    catch 
    { 
    } 
    Console.WriteLine(arg); 
    } 
    public static void Method(out string arg) 
    { 
    arg = "out value"; 
    throw new Exception(); 
    } 
} 

pero este no.

class P 
{ 
    public static void Main() 
    { 
    object[] args = new object[1]; 
    MethodInfo mi = typeof(P).GetMethod("Method"); 
    try 
    { 
     mi.Invoke(null, args); 
    } 
    catch 
    { 
    } 
    Console.WriteLine(args[0]); 
    } 
    public static void Method(out string arg) 
    { 
    arg = "out value"; 
    throw new Exception(); 
    } 
} 

cómo puedo obtener tanto "valor fuera" y un excepción cuando utilizando la reflexión?

+3

Buena pregunta. Pero no deberías confiar en el valor 'out' si arroja un método. –

+1

+1, gran pregunta, por supuesto que tuve que intentarlo :) Especulo que su variable original no pasa a la función invocada, obtiene una copia, y esa copia se refleja de nuevo en el original cuando tiene éxito finalización (que, por supuesto, no sucede). – slugster

+0

@slugster: su especulación es correcta. Sospecho que no hay ninguna manera de hacer eso con la reflexión. –

Respuesta

-1

El parámetro out no está definido si el método arroja una excepción. Puede ver esto al no inicializarlo como nulo en el primer ejemplo, luego el código no se compilará.

Por lo tanto, tiene sentido que el método Invoke no devuelva el valor indefinido si el método arroja una excepción.

+0

"El parámetro out no está definido si el método arroja una excepción". ¿Puedes citar las especificaciones para esto? "Puedes ver esto al no inicializarlo como nulo en el primer ejemplo, luego el código no se compilará". Esto no es una prueba de "indefinición" del resultado. Solo prueba que no está "definitivamente asignado" según las reglas del compilador de C#. "Definitivamente no asignado" no significa indefinido o no asignado. Es una cosa fundamentalmente diferente. –

+2

>> "El parámetro out no está definido si el método arroja una excepción". ¿Porqué es eso? Sí, si elimina la inicialización, el código no se compilará. Pero no debido a la presencia de throw en Method(), sino debido al bloque catch vacío en Main, es decir, no todas las rutas de ejecución inicializan un valor de arg antes del uso real. –

+0

-1, el out var no está indefinido: ejecuta el código y verás. – slugster

1

La excepción omitió el código en MethodInfo.Invoke() que copia el valor [out] del marco de la pila de nuevo en la matriz de objetos. El valor en el marco de pila que creó Invoke() se comporta como lo hace en su primer fragmento. Pero ahí es donde terminan las similitudes.

1

La única forma es sobrecargar su método de forma que represente la posibilidad de una excepción y luego pasar uno en "por las dudas". Lo siguiente produce lo que creo que estás buscando. El problema, tal como lo entiendo, es que la reflexión no realiza una manipulación directa de las direcciones pasadas por referencia. Las direcciones no se ven afectadas hasta que se alcanza el punto final del método sin excepción. Posiblemente una protección de memoria o esquema de seguridad de memoria de MS.

class P 
    { 
     public static void Main() 
     { 
      object[] args = { "1", new Exception()}; 
      MethodInfo mi = typeof(P).GetMethod("Method"); 
      try 
      { 
       mi.Invoke(null, args); 
      } 
      catch 
      { 
      } 
      Console.WriteLine(args[0].ToString()); 
      Console.WriteLine(args[1].ToString()); 
     } 
     public static void Method(ref string arg, ref Exception ex) 
     { 
      try 
      { 
       arg = "out value"; 
       throw new Exception(); 
      } 
      catch (Exception exc) 
      { 
       ex = exc; 
      } 
     } 
} 
+0

No creo que esto resuelva el problema fundamental sin embargo. Si tuviéramos ese tipo de control para el método invocado, no usaríamos la reflexión. El punto es que ese tipo de método podría existir en cualquier otro lugar y podríamos necesitar llamarlo usando reflexión. ¿Cómo haríamos eso? No creo que eso sea posible. –

+0

Acepto que no es posible de la manera descrita. Obtuve la sensación de que él sí tenía el control del método reflejado, así que pensé que propondría una solución alternativa. –

0

Propongo cambiar el método para devolver el objeto Result en lugar del parámetro out. El objeto de resultado puede contener una excepción y también el valor de su arg.

+0

Como noté en el comentario en la respuesta de @ Joel, la pregunta es fundamental. ¿Qué pasa si no tenemos ningún control sobre el método de destino? Existe un método arbitrario y debemos llamarlo usando reflexión. ¿Cómo haríamos eso? –

0

Si el problema es cómo se da cuenta de que se ha producido una excepción y está trabajando con una aplicación de Windows Forms, ¿ha intentado buscar en el evento de excepción de subprocesos y combinarlo con SetUnhandledExceptionMode()?

Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException); 
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); 

static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)   
{    
    HandleException(e.Exception);   
} 
+0

¿Dónde está el valor del parámetro de salida? Creo que malinterpretaste la pregunta. No se trata de atrapar la excepción. El problema es que, al usar la reflexión, si el destinatario lanza, el valor del parámetro de salida no está asignado. Esto no resuelve el problema. –

Cuestiones relacionadas