2010-01-20 13 views
12

Dieser Code gibt "out value" aus.Wie erhält man einen Wert über einen out/ref-Parameter von einer Methode, die eine Ausnahme auslöst?

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

aber dieses tut nicht.

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

wie kann ich sowohl "out-Wert" und eine Ausnahme wenn Reflexion mit?

+3

Schöne Frage. Aber Sie sollten sich nicht auf den "out" -Wert verlassen, wenn eine Methode auslöst. –

+1

+1, gute Frage, natürlich musste ich es versuchen :) Ich würde spekulieren, dass Ihre ursprüngliche Variable nicht in die aufgerufene Funktion übergeben wird, es erhält eine Kopie, und diese Kopie wird bei erfolgreicher Umsetzung ins Original zurückreflektiert Fertigstellung (was natürlich nicht passiert). – slugster

+0

@Slugster: Ihre Spekulation ist richtig. Ich vermute, dass es keine Möglichkeit gibt, das durch Nachdenken zu tun. –

Antwort

-1

Der Parameter out ist nicht definiert, wenn die Methode eine Ausnahme auslöst. Sie können dies sehen, indem Sie es im ersten Beispiel nicht auf Null initialisieren, dann wird der Code nicht kompiliert.

Es ist also sinnvoll, dass die Invoke-Methode den undefinierten Wert nicht zurückgibt, wenn die Methode eine Ausnahme auslöst.

+0

"Der out-Parameter ist nicht definiert, wenn die Methode eine Ausnahme auslöst." Kannst du die Spezifikation dafür angeben? "Sie können dies sehen, indem Sie es im ersten Beispiel nicht auf Null initialisieren, dann wird der Code nicht kompiliert." Dies ist kein Beweis für "Unbestimmtheit" des Ergebnisses. Es beweist nur, dass es gemäß den C# -Compiler-Regeln nicht "definitiv zugewiesen" ist. "Nicht definitiv zugewiesen" bedeutet nicht undefiniert oder nicht zugewiesen. Es ist eine grundlegend andere Sache. –

+2

>> "Der out-Parameter ist nicht definiert, wenn die Methode eine Ausnahme auslöst." Warum das? Ja, wenn Sie die Initialisierung entfernen, wird der Code nicht kompiliert. Aber nicht wegen des Vorhandenseins von throw in Method(), sondern wegen des leeren catch-Blocks in Main, d. H. Nicht alle Ausführungspfade initialisieren einen Wert von arg vor der tatsächlichen Verwendung. –

+0

-1, die out var ist nicht undefiniert - führen Sie den Code und Sie werden sehen. – slugster

1

Die Ausnahme hat den Code in MethodInfo.Invoke() umgangen, der den [out] -Wert vom Stapelrahmen zurück in das Objekt-Array kopiert. Der Wert des Stack-Frames, den Invoke() erstellt hat, verhält sich genau wie im ersten Snippet. Aber hier enden die Gemeinsamkeiten.

1

Die einzige Möglichkeit besteht darin, die Methode so zu überladen, dass die Möglichkeit einer Ausnahme besteht und diese dann "nur für den Fall" übergeben wird. Das Folgende produziert, was ich denke, dass Sie suchen. Das Problem, so wie ich es verstehe, ist, dass die Reflexion keine direkte Manipulation der Adressen durchführt, die per Referenz übergeben werden. Die Adressen sind nicht betroffen, bis der Endpunkt der Methode ohne Ausnahme erreicht wird. Möglicherweise ein Speicherschutz- oder Speichersicherheitsschema von 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

Ich glaube nicht, dass dies das grundlegende Problem löst. Wenn wir diese Art von Kontrolle für die aufgerufene Methode hätten, würden wir keine Reflexion verwenden. Der Punkt ist, dass diese Art von Methode an anderer Stelle existieren könnte und wir sie möglicherweise durch Reflexion nennen müssen. Wie würden wir das machen? Ich denke nicht, dass das möglich ist. –

+0

Ich stimme zu, dass dies nicht in der beschriebenen Weise möglich ist. Ich hatte den Eindruck, dass er die Kontrolle über die reflektierte Methode hatte, daher dachte ich, ich würde einen Workaround vorschlagen. –

0

Ich würde vorschlagen, die Methode so zu ändern, dass Ergebnisobjekt anstelle von out-Parameter zurückgegeben wird. Ergebnisobjekt kann Ausnahme und auch den Wert von arg enthalten.

+0

Wie ich in dem Kommentar in @ Joel's Antwort bemerkt habe, ist die Frage von grundlegender Bedeutung. Was wäre, wenn wir die Zielmethode nicht kontrollieren könnten? Eine beliebige Methode ist da und wir müssen sie mit Reflektion bezeichnen. Wie würden wir das machen? –

0

Wenn das Problem ist, wie Sie feststellen, dass eine Ausnahme aufgetreten ist und Sie mit einer Windows Forms-Anwendung arbeiten, haben Sie versucht, das Thread-Ausnahmeereignis zu betrachten und es mit dem SetUnhandledExceptionMode() zu kombinieren?

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

Wo ist der Wert des Ausgabeparameters? Ich denke du hast die Frage falsch verstanden. Es geht nicht darum, die Ausnahme zu fangen. Das Problem besteht darin, dass der Wert des Ausgabeparameters bei der Verwendung von Reflektion nicht zugewiesen wird. Dies löst das Problem nicht. –