2010-10-08 9 views
15

Mam metodę jak ...zadeklarować, że metoda zawsze zgłasza wyjątek?

int f() { 
    try { 
    int i = process(); 
    return i; 
    } catch(Exception ex) { 
    ThrowSpecificFault(ex); 
    } 
} 

To daje błąd kompilatora, „nie wszystkie ścieżki kodu zwracają wartość”. Ale w moim przypadku ThrowSpecificFault() zawsze wyrzuci (odpowiedni) wyjątek. Więc jestem zmuszony do postawienia wartości zwrotu na końcu, ale to jest brzydkie.

Celem tego wzoru jest przede wszystkim to, że "process()" to połączenie z zewnętrzną usługą sieciową, ale trzeba tłumaczyć wiele różnych wyjątków, aby dopasować oczekiwany interfejs klienta (według schematu fasady) .

Czy można to zrobić w jakiś czystszy sposób?

+1

Powiązane: [Czy istnieje standardowy atrybut "never returns" dla funkcji C#?] (Http://stackoverflow.com/questions/1999181/is-there-a-standard-never-returns-attribute-for-c -functions) –

Odpowiedz

47

sugeruję konwertować ThrowSpecificFault(ex) do throw SpecificFault(ex); metoda SpecificFault zwróci obiekt wyjątku, który ma być wygenerowany, zamiast go samemu wyrzucać. Znacznie czystsze.

Jest to wzorzec zalecany przez Microsoft's guidelines (znajdź tekst "Użyj metod konstruktora wyjątków").

+5

+1, jest to wzorzec zalecany w wytycznych Microsoftu. – Joe

+0

Czy masz link? – noctonura

+2

Mam: http://msdn.microsoft.com/en-us/library/seyhszts.aspx; znajdź tekst "Użyj metod konstruktora wyjątków". – CesarGon

3

nr

Wyobraźmy sobie ThrowSpecificFault zostały określone w oddzielnym DLL. Jeśli zmodyfikujesz bibliotekę DLL, aby nie powodowała wyjątku, uruchom program bez ponownej kompilacji, co by się stało?

+3

Wyobraź sobie, że metoda Foo została zdefiniowana w oddzielnej bibliotece DLL.Jeśli zmodyfikujesz Foo, aby zwrócić długi zamiast int, to uruchom program, który wywołuje Foo bez rekompilacji, co by się stało? *Nic dobrego*. Zmiana * sygnatury metody w zewnętrznej bibliotece jest * nigdy * poprawna, a następnie kontynuuj używanie jej bez rekompilacji. Właśnie dlatego mamy stempel wersji itp. Na zespołach. –

+0

@Eric - Wierzę, że hipotetyczna sytuacja SLaksa nie wymaga zmiany podpisu, więc nie jest to oczywiście przełomowa zmiana. – kvb

+2

@kvb: Proponowana funkcja, jak rozumiem, polega na uchwyceniu faktu, że metoda nigdy nie zwraca * w swoim podpisie *. –

8

Problem polega na tym, że po przejściu do bloku f() Twoja funkcja nigdy nie zwróci wartości. Spowoduje to błąd, ponieważ zadeklarowałeś swoją funkcję jako int, co oznacza, że ​​powiedziałeś kompilatorowi, że twoja metoda zwróci liczbę całkowitą.

Poniższy kod zrobi to, czego szukasz i zawsze zwraca liczbę całkowitą.

int f() { 
    int i = 0; 
    try { 
    i = process(); 

    } catch(Exception ex) { 
    ThrowSpecificFault(ex); 
    } 
    return i; 
} 

umieścić oświadczenie zwrotu na końcu swojej funkcji i będzie w porządku.

Zawsze dobrze jest upewnić się, że twoja metoda zawsze zwróci wartość bez względu na to, jaką ścieżkę wykonawczą przechodzi twoja aplikacja.

+0

+1 pokonałeś mnie przez chwilę !! –

1

Jak o:

int f() { 
int i = -1; 
try { 
    i = process();  
} catch(Exception ex) { 
    ThrowSpecificFault(ex); 
} 
return i; 
} 
+0

Zostawiam to jako odpowiedź, ale Robert Greiner mnie do tego pobił. –

2

Masz trzy opcje:

zawsze zwraca mi ale wstępnie zadeklarować go:

int f() { 
    int i = 0; // or some other meaningful default 
    try { 
     i = process(); 
    } catch(Exception ex) { 
     ThrowSpecificFault(ex); 
    } 
    return i; 
} 

Return wyjątek od metody i rzucać że:

int f() { 
    try { 
     int i = process(); 
     return i; 
    } catch(Exception ex) { 
     throw GenerateSpecificFaultException(ex); 
    } 
} 

Lub utwórz niestandardową klasę wyjątków i wyrzuć:

int f() { 
    try { 
     int i = process(); 
     return i; 
    } catch(Exception ex) { 
     throw new SpecificFault(ex); 
    } 
} 
0

Tak.

Nie należy oczekiwać, że ThrowSpecificFault() zwróci wyjątek.Zwróć wyjątek, a następnie wyślij go tutaj.

To ma więcej sensu. Nie używasz wyjątków dla "normalnego" przepływu, więc jeśli za każdym razem wyrzucisz wyjątek, wyjątek staje się regułą. Tworzenie specyficzny wyjątek w funkcji i rzucać go tutaj, ponieważ jest to wyjątek od przepływu tutaj ..

0

Przypuszczam, że można zrobić ThrowSpecificFault zwrócić obiekt, a następnie można

return ThrowSpecificFault(ex)

W przeciwnym razie, możesz przepisać ThrowSpecificFault jako konstruktor podtypu Exception lub możesz po prostu przekształcić ThrowSpecificFault w fabrykę, która tworzy wyjątek, ale go nie wyrzuca.

3

Można zrobić tak:

catch (Exception ex) 
{ 
    Exception e = CreateSpecificFault(ex); 
    throw e; 
} 
0

w was sprawy, ale nie to jest Twoja wiedza kompilator. Można teraz powiedzieć, że ta metoda na pewno rzuci jakiś nieprzyjemny wyjątek.

Spróbuj

int f() { 
    try { 
    return process(); 
    } catch(Exception ex) { 
    ThrowSpecificFault(ex); 
    } 
    return -1; 
} 

Można również użyć słowa kluczowego throw

int f() { 
    try { 
    return process(); 
    } catch(Exception ex) { 
    throw ThrowSpecificFault(ex); 
    } 
} 

Ale ta metoda powinna powrócić jakiś wyjątek zamiast rzucania go.

0

Użyj Unity.Interception, aby oczyścić kod. Obchodzenia się z podsłuchem, kod może wyglądać następująco:

int f() 
{ 
    // no need to try-catch any more, here or anywhere else... 
    int i = process(); 
    return i; 
} 


Wszystko, co trzeba zrobić w następnym krokiem jest zdefiniowanie obsługi przechwytywania, który można niestandardowy dostosowanych do obsługi wyjątków. Za pomocą tego modułu obsługi można obsłużyć wszystkie wyjątki zgłoszone w aplikacji. Plusem jest to, że nie musisz już oznaczać całego swojego kodu blokami try-catch.

public class MyCallHandler : ICallHandler, IDisposable 
{ 
    public IMethodReturn Invoke(IMethodInvocation input, 
     GetNextHandlerDelegate getNext) 
    { 
     // call the method 
     var methodReturn = getNext().Invoke(input, getNext); 

     // check if an exception was raised. 
     if (methodReturn.Exception != null) 
     { 
      // take the original exception and raise a new (correct) one... 
      CreateSpecificFault(methodReturn.Exception); 

      // set the original exception to null to avoid throwing yet another 
      // exception 
      methodReturn.Exception = null; 
     } 

     // complete the invoke... 
     return methodReturn; 
    } 
} 

Zarejestrowanie klasy w programie obsługi można wykonać za pomocą pliku konfiguracyjnego lub programowo. Kod jest dość prosty. Po rejestracji możesz wystąpienia swoich obiektów za jednością, tak:

var objectToUse = myUnityContainer.Resolve<MyObjectToUse>(); 

Więcej na Unity.Interception:

http://msdn.microsoft.com/en-us/library/ff646991.aspx

7

Teraz rodzajem powrotu może być typu, czyli „void”, czyli "brak typu zwrotu". Możemy teoretycznie dodać drugi specjalny typ zwrotu "never", który ma semantykę, której potrzebujesz. Punkt końcowy instrukcji wyrażenia składający się z wywołania metody "nigdy nie zwracającej" byłby uważany za nieosiągalny, a zatem byłby legalny w każdym kontekście C#, w którym "goto", "rzut" lub "powrót" jest legalny .

Jest mało prawdopodobne, że zostanie on dodany do systemu typów już za dziesięć lat. Następnym razem, gdy projektujesz system typów od podstaw, pamiętaj o dodaniu typu "nigdy".

Powiązane problemy