2009-08-20 10 views
15

This question is close to what I want to do, ale nie do końca.Czy można złapać więcej niż jeden typ wyjątku dla każdego bloku?

Czy istnieje sposób na uproszczenie następującego kodu?

private bool ValidDirectory(string directory) 
{ 
    if (!Directory.Exists(directory)) 
    { 
     if (MessageBox.Show(directory + " does not exist. Do you wish to create it?", this.Text) 
      == DialogResult.OK) 
     { 
      try 
      { 
       Directory.CreateDirectory(directory); 
       return true; 
      } 
      catch (IOException ex) 
      { 
       lblBpsError.Text = ex.Message; 
      } 
      catch (UnauthorizedAccessException ex) 
      { 
       lblBpsError.Text = ex.Message; 
      } 
      catch (PathTooLongException ex) 
      { 
       lblBpsError.Text = ex.Message; 
      } 
      catch (DirectoryNotFoundException ex) 
      { 
       lblBpsError.Text = ex.Message; 
      } 
      catch (NotSupportedException ex) 
      { 
       lblBpsError.Text = ex.Message; 
      } 
     } 
    } 

    return false; 
} 

Wydaje się odpady, a jeśli później chcą zmienić sposób mogę zgłosić błąd z powrotem do użytkownika, czy może chcę zalogować się tych błędów, czy cokolwiek, to muszę zmienić 5 różnych łapać bloki. Czy czegoś brakuje, czy też jest to rażąco przeciwko ponownemu użyciu kodu?

Czy staram się być (zbyt) leniwy?

Odpowiedz

23

Można użyć:

catch (SystemException ex) 
{ 
    if( (ex is IOException) 
     || (ex is UnauthorizedAccessException) 
// These are redundant 
// || (ex is PathTooLongException) 
// || (ex is DirectoryNotFoundException) 
     || (ex is NotSupportedException) 
    ) 
     lblBpsError.Text = ex.Message; 
    else 
     throw; 
} 
+1

To może być nieco uproszczone - usuń DirectoryNotFoundException i PathTooLongException, ponieważ (ex jest wyjątek IOException) zwróci true dla nich –

+0

Nie, nie będzie. Operator 'is' jest bardzo dokładny, nie zwróci wartości true dla podklas, tylko ta dokładna klasa. Też to pomyślałem, a potem przetestowałem w LINQPad. –

+1

Matthew, następujący kod testowy zwraca true. Nie jestem pewien, dlaczego masz inny wynik ... spróbuj { rzucić nowy DirectoryNotFoundException(); } catch (Exception ex) { return (ex jest wyjątkiem IOException); } –

0

Można zrobić

ex.GetType()

widzieć http://msdn.microsoft.com/en-us/library/system.exception.gettype.aspx

EDIT

try    
{     
    Directory.CreateDirectory(directory); 
    return true;   
}    
catch (Exception ex)    
{ switch(ex.GetType()) 
     case ..... 
     case .......... 
    blBpsError.Text = ex.Message;    
} 
+1

To wymagałoby złapania 'wyjątku', ale tak, to jedno rozwiązanie. –

+0

Czy przełącznik typu i domyślny może po prostu wyrzucić go ponownie? – qui

+0

Złapiesz wyjątek, ale możesz go zawsze wyrzucić, jeśli nie jest to typ, którym się posługujesz. – Keith

8

Jeśli wyjątki mają wspólny superklasę następnie można po prostu złapać superklasę .

+1

Nie robią tego. Lub raczej nie mają wspólnej superklasy, której żaden inny możliwy wyjątek nie chciałbym obsłużyć. –

0

Można złapać wyjątek klasy bazowej (wszystkie wyjątki wynikają z SystemException):

try 
{ 
    Directory.CreateDirectory(directory); 
    return true; 
} 
catch (SystemException ex) 
{ 
    lblBpsError.Text = ex.Message; 
} 

Ale wtedy może skończyć się łapanie wyjątków nie chcesz złapać.

+0

Problem polega na tym, że 'ArgumentException's to także' SystemException'', a ja konkretnie nie chcę ich złapać, ponieważ to jest mój problem, a nie użytkownicy. –

+0

Następnie wystarczy dodać gałąź catch, która przechwytuje ArgumentExceptions przed gałęzią, która przechwytuje wyjątek (lub czy kompilator optymalizuje próby catch?) – RobV

+0

Uruchamia je w kolejności. Jestem dość pewny, że co najmniej nie zostanie zoptymalizowany. Ale wtedy wracasz do posiadania trzech instrukcji catch, które czysto 'throw;' –

3

Tak, starasz się być leniwy, ale lenistwo jest jednym z the virtues of a programmer, więc to jest dobre.

chodzi o twoje pytanie: nie ma sposobu Jestem świadomy, ale istnieją pewne obejścia dostępne:

  • dać wyjątki wspólnego przodka. Myślę, że nie będzie to możliwe w twoim przypadku, ponieważ wydają się być wbudowane.
  • Złap najbardziej ogólny wyjątek, jaki możesz.
  • Przenieś kod obsługi do swojej własnej funkcji i wywołaj go z każdego bloku catch.
+0

Dla przypomnienia, wszystkie z nich są wbudowane, podobnie jak wywoływana jest metoda. –

+0

Thx, zaktualizowana odpowiedź – soulmerge

2

Jest to denerwujące, a inne odpowiedzi sugerują dobre obejścia (użyłbym @ Lotfi's).

Jednak takie zachowanie jest wymagane z uwagi na bezpieczeństwo typu C#.

Załóżmy, że może to zrobić:

try 
{ 
    Directory.CreateDirectory(directory); 
    return true; 
} 
catch (IOException, 
    UnauthorizedAccessException, 
    PathTooLongException, 
    DirectoryNotFoundException, 
    NotSupportedException ex) 
{ 
    lblBpsError.Text = ex.Message; 
} 

Teraz, jakiego rodzaju jest ex? Wszystkie mają .Message, ponieważ dziedziczą System.Exception, ale spróbuj uzyskać dostęp do dowolnej z ich innych właściwości i masz problem.

+0

Musisz użyć najniższego wspólnego mianownika. Możesz chwytać tylko pochodne 'wyjątków', więc ta droga nie ma prawdziwych problemów. –

+0

W tym przypadku skończy się wyjątek SystemException. Kompilator może to rozwiązać, więc bezpieczeństwo typu nie jest również zablokowane. –

+0

Kompilator mógłby, i IDE też mógł, ale byłby nieco zaciemniony dla programisty - czego intellisense powinni oczekiwać na ex? Widzę sposób, że coś takiego może działać - coś takiego jak wsparcie dla kontrynariancji w C# 4 – Keith

2

Należy również zauważyć, że przy wychwytywaniu więcej niż jednego rodzaju wyjątku powinny być one uporządkowane według większości ogólnych.Wyjątek znajdzie pierwszą na liście, która pasuje i rzuci ten błąd, żaden z pozostałych błędów nie zostanie zgłoszony.

+0

Po napisaniu tego zauważyłem, że IOException jest klasą podstawową dla niektórych z nich, ale jest pierwszą na powyższej liście. Nie ma znaczenia dla tego kodu, ale bardzo dobry punkt. –

0

Można użyć delegatów, zrobi to, co chcesz:

EDIT: uproszczone nieco

static void Main(string[] args) 
{ 
    TryCatch(() => { throw new NullReferenceException(); }, 
     new [] { typeof(AbandonedMutexException), typeof(ArgumentException), typeof(NullReferenceException) }, 
     ex => Console.WriteLine(ex.Message)); 

} 

public static void TryCatch(Action action, Type[] exceptions, Action<Exception> catchBlock) 
{ 
    try 
    { 
     action(); 
    } 
    catch (Exception ex) 
    { 
     if(exceptions.Any(p => ex.GetType() == p)) 
     { 
      catchBlock(ex); 
     } 
     else 
     { 
      throw; 
     } 
    } 
} 

Twój szczególności try/catch byłoby:

bool ret; 
TryCatch(
    () => 
     { 
      Directory.CreateDirectory(directory); 
      ret = true; 
     }, 
    new[] 
     { 
      typeof (IOException), typeof (UnauthorizedAccessException), typeof (PathTooLongException), 
      typeof (DirectoryNotFoundException), typeof (NotSupportedException) 
     }, 
    ex => lblBpsError.Text = ex.Message 
); 

return ret; 
0

I zrozum, że niektóre z tych wyjątków mogą nie być możliwe do przewidzenia, ale jeśli to możliwe, spróbuj wdrożyć własną logikę "wyprzedzającą". Wyjątki są drogie, ale w tym przypadku prawdopodobnie nie są złamaniem umowy.

Na przykład użyj Directory.GetAccessControl (...) zamiast polegać na wyrzucaniu wyjątku UnauthorizedAccessException.

+0

Wyjątki nie są tak drogie, aby być człowiekiem zauważalnym. Masz rację, w tym przypadku zdecydowanie nie jest to złamanie umowy, a więcej niż prawdopodobne, nigdy się nie wydarzy, wolałbym raczej nie awarię, jeśli tak się stanie. –

2

Tylko dla kompletności, albowiem

w VB, można użyć warunkową obsługę wyjątków:

Try 
    … 
Catch ex As Exception When TypeOf ex Is MyException OrElse _ 
          TypeOf ex Is AnotherExecption 
    … 
End Try 

Taki Catch blok dostanie się jedynie do określonych wyjątków - w przeciwieństwie do C#.

Być może przyszła wersja C# będzie oferować podobną funkcję (w końcu istnieje specjalna instrukcja IL dla tego kodu).

MSDN: How to: Filter Errors in a Catch Block in Visual Basic

Powiązane problemy