Po co wreszcie używać w C #?

189

Cokolwiek jest w środku, bloki są zawsze wykonywane (prawie) zawsze, więc jaka jest różnica między umieszczeniem w nim kodu a pozostawieniem go niezamkniętego?

Rodrigo
źródło
3
Co masz na myśli mówiąc, że pozostawiasz niezamknięty?
Ramesh
5
A co rozumiesz przez „(prawie)”?
Beska
49
Jeśli wyciągniesz przewód zasilający, gdy maszyna wykonuje klauzulę try, klauzula ostatecznie nie zostanie wywołana.
Dour High Arch
5
lol, tak, to prawda, ale tak naprawdę nie możesz tego kodować, prawda?
Ed S.
2
@Ed: Użyj transakcji. Twoja klauzula try musi wprowadzić jakąś tymczasową lub w pamięci zmianę, którą można utrwalić w pojedynczej, atomowej zmianie w klauzuli ostatecznie. Jest to rzadko łatwe i może wymagać specjalnego sprzętu.
Dour High Arch

Odpowiedzi:

405

Kod w bloku wreszcie zostanie wykonany niezależnie od tego, czy istnieje wyjątek. Jest to bardzo przydatne, jeśli chodzi o niektóre funkcje porządkowe, które musisz zawsze działać jak zamykanie połączeń.

Zgaduję, że twoje pytanie brzmi: dlaczego powinieneś to zrobić:

try
{
    doSomething();
}
catch
{
    catchSomething();
}
finally
{
    alwaysDoThis();
}

Kiedy możesz to zrobić:

try
{
    doSomething();
}
catch
{
    catchSomething();
}

alwaysDoThis();

Odpowiedź jest taka, że ​​wiele razy kod w instrukcji catch zwróci wyjątek lub wyłączy się z bieżącej funkcji. W tym ostatnim kodzie „alwaysDoThis ();” Wywołanie nie zostanie wykonane, jeśli kod w instrukcji catch wyświetli zwrot lub zgłosi nowy wyjątek.

Kevin Pang
źródło
3
Hmm Bardzo podobny do tego, co powiedziałem, ale jaśniej i dokładniej. Określony +1.
Beska
46
dotyczy to również „powrotu” w bloku try {}.
Lucas
4
w rzeczywistości ma to zastosowanie nawet bez bloku catch {} (po prostu spróbuj / w końcu, pozwolenie na wyjątki wybuchają)
Lucas
lepiej niż mój, był w pracy i nie chciał spędzać dziesięciu minut na szczegółowej odpowiedzi. +1
Matt Briggs
2
Tak, dokładnie to miałem na myśli: D Teraz to rozumiem.
Rodrigo
62

Wskazano już na większość zalet używania try-wreszcie, ale pomyślałem, że dodam ten:

try
{
    // Code here that might throw an exception...

    if (arbitraryCondition)
    {
        return true;
    }

    // Code here that might throw an exception...
}
finally
{
    // Code here gets executed regardless of whether "return true;" was called within the try block (i.e. regardless of the value of arbitraryCondition).
}

Takie zachowanie sprawia, że bardzo przydatne w różnych sytuacjach, szczególnie gdy trzeba wykonać czyszczenia (zasoby wrzucać), choć przy użyciu bloku jest często lepiej w tym przypadku.

Noldorin
źródło
2
Jest to dosłownie jedyny powód, dla którego wreszcie używam
Christopher Townsend
12

za każdym razem, gdy korzystasz z niezarządzanych żądań kodu, takich jak czytniki strumieniowe, żądania db itp. i chcesz złapać wyjątek, a następnie użyj try catch w końcu i zamknij strumień, czytnik danych itp. w końcu, jeśli nie zrobisz tego, gdy wystąpi błąd, połączenie nie zostanie zamknięte, to naprawdę źle z żądaniami db

 SqlConnection myConn = new SqlConnection("Connectionstring");
        try
        {
            myConn.Open();
            //make na DB Request                
        }
        catch (Exception DBException)
        {
            //do somehting with exception
        }
        finally
        {
           myConn.Close();
           myConn.Dispose();
        }

jeśli nie chcesz złapać błędu, użyj

 using (SqlConnection myConn = new SqlConnection("Connectionstring"))
        {
            myConn.Open();
            //make na DB Request
            myConn.Close();
        }

a obiekt połączenia zostanie usunięty automatycznie, jeśli wystąpi błąd, ale błąd nie zostanie przechwycony

Bob The Janitor
źródło
2
Dispose () spowoduje również zamknięcie () połączenia, bez potrzeby wywoływania obu. Close () NIE Dipose (), możesz ponownie otworzyć połączenie.
Lucas
Fajnie, dzięki za wzmiankę o użyciu W przeciwnym razie musiałbym odpowiedzieć.
Dan Rosenstark,
11

Ponieważ w końcu zostanie wykonany, nawet jeśli nie obsłużysz wyjątku w bloku catch.

Matt Briggs
źródło
7

Wreszcie instrukcje mogą być wykonywane nawet po powrocie.

private int myfun()
{
    int a = 100; //any number
    int b = 0;
    try
    {
        a = (5 / b);
        return a;
    }
    catch (Exception ex)
    {
        Response.Write(ex.Message);
        return a;
    }

 //   Response.Write("Statement after return before finally");  -->this will give error "Syntax error, 'try' expected"
    finally
    {
      Response.Write("Statement after return in finally"); // --> This will execute , even after having return code above
    } 

    Response.Write("Statement after return after finally");  // -->Unreachable code
}
Prateek Gupta
źródło
7

finally, jak w:

try {
  // do something risky
} catch (Exception ex) {
  // handle an exception
} finally {
  // do any required cleanup
}

to gwarantowana możliwość wykonania kodu po twoim try..catch bloku, bez względu na to, czy blok try rzucił wyjątek.

To sprawia, że ​​jest idealny do takich rzeczy, jak uwalnianie zasobów, połączenia db, uchwyty plików itp.

David Alpert
źródło
3
Wszystkie te przykłady są zazwyczaj lepiej wyświetlane z blokiem używającym, ale tak naprawdę nie umniejsza to twojej odpowiedzi.
Joel Coehoorn
4

wyjaśnię użycie w końcu z wyjątkiem czytnika plików Przykład

  • bez użycia w końcu
try{

  StreamReader strReader = new StreamReader(@"C:\Ariven\Project\Data.txt");
  Console.WriteLine(strReader.ReadeToEnd());
  StreamReader.Close();
}
catch (Exception ex)
{
  Console.WriteLine(ex.Message);
}

w powyższym przykładzie, jeśli plik o nazwie dane.txt brakuje wyjątek zostanie wyrzucony i będą obsługiwane, ale oświadczenie o nazwie StreamReader.Close();nigdy nie zostanie wykonana.
Z tego powodu zasoby związane z czytnikiem nigdy nie zostały wydane.

  • Aby rozwiązać powyższy problem, używamy w końcu
StreamReader strReader = null;
try{
    strReader = new StreamReader(@"C:\Ariven\Project\Data.txt");
    Console.WriteLine(strReader.ReadeToEnd());
}
catch (Exception ex){
    Console.WriteLine(ex.Message);
}
finally{
    if (strReader != null){
        StreamReader.Close();
    }
}

Happy Coding :)

Uwaga: „@” służy do tworzenia ciągów ciągłych , aby uniknąć błędu „Nierozpoznana sekwencja ucieczki”. Symbol @ oznacza dosłowne odczytanie tego ciągu i w przeciwnym razie nie należy interpretować znaków kontrolnych.

Ariven Nadar
źródło
2

Załóżmy, że musisz ustawić kursor z powrotem na domyślny wskaźnik zamiast kursora oczekującego (klepsydra). Jeśli wyjątek zostanie zgłoszony przed ustawieniem kursora i nie spowoduje całkowitego zawieszenia aplikacji, możesz pozostać z mylącym kursorem.

Chris Doggett
źródło
2

Czasami nie chcesz obsłużyć wyjątku (brak bloku catch), ale chcesz wykonać kod czyszczenia.

Na przykład:

try
{
    // exception (or not)
}
finally
{
    // clean up always
}
markom
źródło
Jeśli wyjątek nie zostanie przechwycony, wykonanie bloku ostatecznie zależy od tego, czy system operacyjny zdecyduje się wyzwolić operację cofania wyjątku.
Vikas Verma
2

Wreszcie blok jest cenny do czyszczenia wszelkich zasobów przydzielonych w bloku try, a także do uruchamiania dowolnego kodu, który musi zostać wykonany, nawet jeśli istnieje wyjątek. Kontrola jest zawsze przekazywana do bloku ostatecznie, niezależnie od tego, jak wychodzi blok try.

cgreeno
źródło
1

Ahh ... Myślę, że rozumiem, co mówisz! Zajęło mi to chwilę ... zastanawiasz się "dlaczego umieścić go w bloku na końcu zamiast po bloku w końcu i całkowicie poza try-catch-w końcu".

Na przykład może to być spowodowane tym, że wstrzymujesz wykonywanie, jeśli zgłosisz błąd, ale nadal chcesz wyczyścić zasoby, takie jak otwarte pliki, połączenia z bazą danych itp.

Beska
źródło
1

Kontrolny przepływ ostatniego bloku następuje po bloku Try lub Catch.

[1. First Code]
[2. Try]
[3. Catch]
[4. Finally]
[5. After Code]

z wyjątkiem 1> 2> 3> 4> 5, jeśli 3 ma instrukcję Return 1> 2> 3> 4

bez wyjątku 1> 2> 4> 5, jeśli 2 ma instrukcję return 1> 2> 4

Ranald Fong
źródło
0

Jak wspomniano w dokumentacji :

Powszechnym użyciem catch i wreszcie razem jest uzyskanie i wykorzystanie zasobów w bloku try, poradzenie sobie z wyjątkowymi okolicznościami w bloku catch i uwolnienie zasobów w bloku last.

Warto również przeczytać to , co stanowi:

Po znalezieniu pasującej klauzuli catch system przygotowuje się do przeniesienia kontroli do pierwszej instrukcji klauzuli catch. Przed rozpoczęciem wykonywania klauzuli catch system najpierw wykonuje kolejno wszystkie klauzule ostatecznie powiązane z instrukcjami try bardziej zagnieżdżonymi niż te, które wychwyciły wyjątek.

Jest więc jasne, że kod znajdujący się w finallyklauzuli zostanie wykonany, nawet jeśli poprzednia catchklauzula zawierała returninstrukcję.

Nexaspx
źródło