2010-01-20 10 views
12

ten kod wyświetla "out value".Jak uzyskać wartość za pomocą parametru out/ref z metody, która zgłasza wyjątek?

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

ale ten nie.

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

jaki sposób można uzyskać zarówno "wartość wyjścia" i wyjątek gdy użyciu odbicia?

+3

Ładne pytanie. Ale nie powinieneś polegać na wartości 'out', jeśli metoda rzuca. –

+1

+1, świetne pytanie, oczywiście musiałem spróbować :) Spekulowałbym, że twoja oryginalna zmienna nie zostanie przekazana do wywoływanej funkcji, otrzyma kopię, a ta kopia zostanie ponownie odzwierciedlona w oryginale po pomyślnym ukończenie (co oczywiście nie nastąpi). – slugster

+0

@lesterster: Twoje spekulacje są poprawne. Podejrzewam, że nie ma sposobu, aby to zrobić z refleksją. –

Odpowiedz

-1

Parametr out jest niezdefiniowany, jeśli metoda zgłasza wyjątek. Możesz to zobaczyć, nie inicjując null w pierwszym przykładzie, wtedy kod się nie skompiluje.

A zatem metoda Invoke nie powinna zwracać niezdefiniowanej wartości, jeśli metoda zgłasza wyjątek.

+0

"Parametr out jest niezdefiniowany, jeśli metoda zgłasza wyjątek." Czy możesz przytoczyć specyfikację tego? "Możesz to zobaczyć, nie inicjując null w pierwszym przykładzie, wtedy kod nie będzie się kompilował." Nie jest to dowodem "nieokreśloności" wyniku. Dowodzi to jedynie, że nie jest "definitywnie przypisany" zgodnie z regułami kompilatora C#. "Nieprzypisany" nie oznacza niezdefiniowanego ani nieprzydzielonego. To zasadniczo inna sprawa. –

+2

>> "Parametr out jest niezdefiniowany, jeśli metoda zgłasza wyjątek." Dlaczego? Tak, jeśli usuniesz inicjalizację, kod nie zostanie skompilowany. Ale nie z powodu obecności throw w Method(), ale z powodu pustego bloku catch w Main, tj. Nie wszystkie ścieżki wykonania inicjują wartość arg przed rzeczywistym użyciem. –

+0

-1, out var nie jest niezdefiniowany - uruchom kod, a zobaczysz. – slugster

1

Wyjątek pominął kod w MethodInfo.Invoke(), który kopiuje wartość [out] z ramki stosu z powrotem do tablicy obiektów. Wartość na ramce stosu, którą wywołał Invoke(), zachowuje się tak, jak w pierwszym filtrze. Ale właśnie tam kończą się podobieństwa.

1

Jedynym sposobem jest przeciążenie metody w sposób uwzględniający możliwość wystąpienia wyjątku, a następnie przekazanie jej "na wszelki wypadek". Poniższe produkuje to, co myślę, że szukasz. Jak rozumiem, problem polega na tym, że odbicie nie wykonuje bezpośredniej manipulacji adresami przekazanymi przez odniesienie. Adresy nie zostaną zmienione, dopóki nie zostanie osiągnięty punkt końcowy metody bez wyjątku. Prawdopodobnie schemat ochrony pamięci lub pamięci z 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

Nie sądzę, że rozwiązuje to podstawowy problem. Gdybyśmy mieli tego rodzaju kontrolę dla przywoływanej metody, nie używalibyśmy refleksji. Chodzi o to, że taka metoda może istnieć gdzie indziej i być może będziemy musieli ją nazwać za pomocą refleksji. Jak byśmy to zrobili? Nie sądzę, że to możliwe. –

+0

Zgadzam się, że nie jest to możliwe w opisany sposób. Miałem wrażenie, że ma on kontrolę nad odbitą metodą, więc pomyślałem, że zaproponuję obejście. –

0

Proponuję zmienić metodę zwracania obiektu wynikowego zamiast parametru out. Obiekt wynikowy może zawierać wyjątek, a także wartość argumentu.

+0

Jak zauważyłem w komentarzu w odpowiedzi Joela, pytanie jest fundamentalne. Co jeśli nie mamy żadnej kontroli nad metodą docelową. Jest tam arbitralna metoda i musimy ją nazwać za pomocą refleksji. Jak byśmy to zrobili? –

0

Jeśli problem polega na tym, jak można złapać wyjątek i pracujesz z aplikacją Windows Forms, czy próbowałeś spojrzeć na wyjątek Thread Exception i połączyć go z 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

Gdzie jest wartość parametru wyjściowego? Myślę, że źle zrozumiałeś pytanie. Nie chodzi o złapanie wyjątku. Problem polega na tym, że podczas korzystania z odbicia, jeśli wywoływany rzuca, wartość parametru wyjściowego nie jest przypisana. To nie rozwiązuje problemu. –

Powiązane problemy