Kapan akhirnya dijalankan jika Anda melempar pengecualian dari catch block?


135
try {
   // Do stuff
}
catch (Exception e) {
   throw;
}
finally {
   // Clean up
}

Di blok di atas kapan akhirnya blok dipanggil? Sebelum melempar e atau akhirnya dipanggil dan kemudian menangkap?


14
ps Anda tidak boleh "membuang e;" karena itu akan mengacaukan jejak stack pengecualian asli. Anda hanya harus "melempar;". Atau buat pengecualian baru dan atur InnerException ke "e" sebelum Anda melemparkannya.
Erv Walter

24
akhirnya akan menjadi pilihan kata kunci yang sangat buruk jika tidak berjalan terakhir , bukan begitu?
Eric Lippert

@ ErrWalter apakah ini masih benar? Saya menguji keduanya di VS2017, dan tampaknya persis sama. Bisakah Anda memberikan lebih banyak informasi atau referensi? Terima kasih
Jeff Puckett

cukup beri nama saran, gunakan Eksepsi cadangan untuk acara / delegasi
mr R

Jawaban:


138

Itu akan dipanggil setelah e dilemparkan kembali (yaitu setelah catch block dieksekusi)

mengedit ini 7 tahun kemudian - satu catatan penting adalah bahwa jika etidak ditangkap oleh blok coba / tangkap lebih jauh dari tumpukan panggilan atau ditangani oleh penangan pengecualian global, maka finallyblok tersebut mungkin tidak akan pernah dieksekusi sama sekali.


18
dan tidak pernah jika Anda menelepon Envrionment.FailFast ()
Johannes Rudolph

16
Setelah mencoba kode dalam jawaban Brandon, saya melihat bahwa finallyTIDAK dijalankan jika pengecualian dilemparkan sebelumnya catchtidak pernah tertangkap dalam sebuah luar try- catchblock!
Andrew

3
Terima kasih telah mengedit jawaban Anda (diterima) untuk memasukkan informasi baru.
Gordon Bean

3
Mereka (Microsoft) membicarakannya di situs dokumentasi baru: docs.microsoft.com/en-us/dotnet/csharp/language-reference/… : "Dalam pengecualian yang ditangani, blok akhirnya yang terkait dijamin akan dijalankan. Namun , jika pengecualian tidak ditangani, eksekusi blok akhirnya tergantung pada bagaimana operasi pelepasan pengecualian dipicu. Itu, pada gilirannya, tergantung pada bagaimana komputer Anda diatur. "
DotNetSparky

1
Perhatikan bahwa "tertangkap oleh blok coba / tangkap lebih jauh di atas tumpukan panggilan" akan mencakup penangan kerangka kerja seperti yang ada di ASP.NET atau pelari uji. Cara yang lebih baik untuk meletakkannya adalah "jika program Anda terus berjalan setelah catch block, maka akhirnya block akan dieksekusi."
ArrowCase

91

Mengapa tidak mencobanya:

outer try
inner try
inner catch
inner finally
outer catch
outer finally

dengan kode (diformat untuk ruang vertikal):

static void Main() {
    try {
        Console.WriteLine("outer try");
        DoIt();
    } catch {
        Console.WriteLine("outer catch");
        // swallow
    } finally {
        Console.WriteLine("outer finally");
    }
}
static void DoIt() {
    try {
        Console.WriteLine("inner try");
        int i = 0;
        Console.WriteLine(12 / i); // oops
    } catch (Exception e) {
        Console.WriteLine("inner catch");
        throw e; // or "throw", or "throw anything"
    } finally {
        Console.WriteLine("inner finally");
    }
}

5
+1, untuk sesuatu yang sangat sederhana, Anda harus mencobanya seperti yang dilakukan Marc. GJ mengilustrasikannya dengan mencoba bersarang / menangkap / akhirnya :)
Allen Rice

1
@ AllenRice mengapa mencobanya jika Marc sudah memilikinya, dan saya bisa mencari di google untuk menemukan jawaban Marc? Atau mungkin lebih baik, coba sendiri, lalu buat pertanyaan SO dan jawab sendiri untuk keuntungan orang lain.
joshden

8
Harap dicatat bahwa jika Anda tidak menangkap pengecualian di tangkapan luar, bagian dalam akhirnya TIDAK PERNAH dieksekusi !! Dalam hal ini, hasilnya adalahouter try inner try inner catch Unhandled Exception: System.DivideByZeroException...
Andrew

1
@Andrew kamu benar. Penjelasannya dapat Anda temukan di sini Majalah MSDN 2008 September: Pemrosesan Pengecualian Tidak Tertangani di CLR (untuk membuka chm, perlu dibuka: Properti File -> Umum -> Buka Kunci). Jika Anda mengganti blok tangkap luar dengan "tangkap (ArgumentException)" tidak ada yang akhirnya memblokir tidak akan diproses juga, karena CLR tidak dapat menemukan "pengendali pengecualian yang setuju untuk menangani pengecualian" DivideByZeroException.
vladimir

35

Setelah membaca semua jawaban di sini, sepertinya jawaban akhirnya tergantung :

  • Jika Anda melemparkan kembali pengecualian di dalam blok tangkap, dan pengecualian itu ditangkap di dalam blok tangkap lain, semuanya dijalankan sesuai dengan dokumentasi.

  • Namun, jika pengecualian re-trown tidak ditangani, akhirnya tidak pernah dieksekusi.

Saya menguji contoh kode ini dalam VS2010 w / C # 4.0

static void Main()
    {
        Console.WriteLine("Example 1: re-throw inside of another try block:");

        try
        {
            Console.WriteLine("--outer try");
            try
            {
                Console.WriteLine("----inner try");
                throw new Exception();
            }
            catch
            {
                Console.WriteLine("----inner catch");
                throw;
            }
            finally
            {
                Console.WriteLine("----inner finally");
            }
        }
        catch
        {
            Console.WriteLine("--outer catch");
            // swallow
        }
        finally
        {
            Console.WriteLine("--outer finally");
        }
        Console.WriteLine("Huzzah!");

        Console.WriteLine();
        Console.WriteLine("Example 2: re-throw outside of another try block:");
        try
        {
            Console.WriteLine("--try");
            throw new Exception();
        }
        catch
        {
            Console.WriteLine("--catch");
            throw;
        }
        finally
        {
            Console.WriteLine("--finally");
        }

        Console.ReadLine();
    }

Berikut hasilnya:

Contoh 1: melempar kembali ke dalam blok try lain:
--outer try
---- inner try
---- inner catch
---- inner akhirnya
--outer catch
--outer akhirnya Huzzah
!

Contoh 2: melemparkan kembali di luar blok percobaan lain:
-
coba --catch

Pengecualian Tidak Tertangani: System.Exception: Pengecualian jenis 'System.Exception' dilemparkan.
di ConsoleApplication1.Program.Main () dalam C: \ sumber lokal \ ConsoleApplication1 \ Program.cs: baris 53


3
Hasil tangkapan bagus, saya tidak menyadarinya!
Andrew

1
Perhatikan bahwa yang terakhir akhirnya dapat berjalan, tergantung pada apa yang Anda pilih: stackoverflow.com/a/46267841/480982
Thomas Weller

1
Menarik ... Pada .NET Core 2.0, bagian terakhir berjalan setelah pengecualian tidak tertangani.
Mahdi Ghiasi

Cukup menarik, saya baru saja melakukan tes di keduanya .NET Core 2.0 dan .NET Framework 4.6.1 dan keduanya menjalankan yang terakhir setelah pengecualian yang tidak tertangani. Apakah perilaku ini berubah?
Cameron Bielstein

24

Contoh Anda akan berperilaku identik dengan kode ini:

try {
    try {
        // Do stuff
    } catch(Exception e) {
        throw e;
    }
} finally {
    // Clean up
}

Sebagai catatan tambahan, jika Anda benar-benar bermaksud throw e;(yaitu, melemparkan pengecualian yang sama dengan yang baru Anda tangkap), itu jauh lebih baik untuk dilakukan throw;, karena itu akan mempertahankan jejak tumpukan asli daripada membuat yang baru.


Saya tidak berpikir ini benar. Akhirnya harus berada di dalam blok percobaan luar bukan di luarnya
Matthew Pigram

@ MatthewPigram: Apa maksudmu? The finallyblok pada kenyataannya akan berjalan setelah catchblok (bahkan jika blok catch rethrows pengecualian), yang adalah apa potongan saya sedang mencoba untuk menggambarkan.
Daniel Pryden

Dari cara saya menginterpretasikan contohnya, ia mencoba melakukan try catch pada akhirnya di dalam blok try lainnya. BUKAN try catch dalam catch catch akhirnya
Matthew Pigram

1
@ MatthewPigram: Jawaban saya sama sekali tidak memiliki konstruksi "coba-tangkap-akhirnya". Ia memiliki "coba-akhirnya", dan di dalam tryblok itu jawaban saya memiliki "coba-coba". Saya mencoba menjelaskan perilaku konstruksi 3 bagian dengan menggunakan dua konstruksi 2 bagian. Saya tidak melihat tanda-tanda tryblok kedua dalam pertanyaan awal, jadi saya tidak mengerti dari mana Anda mendapatkan itu.
Daniel Pryden

12

Jika ada pengecualian yang tidak tertangani di dalam blok handler, blok akhirnya dipanggil tepat nol kali

  static void Main(string[] args)
  {
     try
     {
        Console.WriteLine("in the try");
        int d = 0;
        int k = 0 / d;
     }
     catch (Exception e)
     {
        Console.WriteLine("in the catch");
        throw;
     }
     finally
     {
        Console.WriteLine("In the finally");
     }
  }

Keluaran:

C: \ users \ administrator \ document \ TestExceptionNesting \ bin \ Release> TestExceptionNesting.exe

di coba

dalam tangkapan

Pengecualian Tidak Tertangani: System.DivideByZeroException: Mencoba untuk membagi dengan nol. di TestExceptionNesting.Program.Main (String [] args) di C: \ users \ administrator \ document \ TestExceptionNesting \ TestExceptionNesting.cs: line 22

C: \ users \ administrator \ document \ TestExceptionNesting \ bin \ release>

Saya mendapat pertanyaan ini hari ini di sebuah wawancara dan pewawancara terus kembali "apakah Anda yakin akhirnya tidak dipanggil?" Saya tidak yakin apakah itu berarti pertanyaan jebakan atau pewawancara memiliki sesuatu yang lain dalam pikiran saya dan menulis kode yang salah untuk saya debug sehingga saya pulang dan mencobanya (membangun dan menjalankan, tidak ada interaksi debugger), hanya untuk menempatkan pikiran saya pada beristirahat.


Kecuali pengecualian yang dilemparkan tertangkap di tangkapan lain di suatu tempat yang lebih tinggi dari tumpukan, dalam hal ini pengecualian dapat dilakukan jika pengecualian yang dilemparkan ditangani ... Atau saya mungkin salah ...
tomosius

@ Tomosius, ya, itulah yang dijelaskan jawaban Brandon. :)
Andrew

@ Tomosius Itulah sebabnya saya mulai dengan menentukan "Jika ada pengecualian yang tidak ditangani". Jika pengecualian yang dilemparkan ditangkap di suatu tempat maka menurut definisi kita berbicara tentang kasus yang berbeda.
Eusebio Rufian-Zilbermann

Ini tidak benar. Setidaknya tidak untuk NET Core 3.1. Proyek konsol baru sederhana dengan kode ini menunjukkan "pada akhirnya" setelah pengecualian.
emzero

Menarik bahwa perilakunya telah berubah, .NET core bahkan tidak ada ketika saya memposting;)
Eusebio Rufian-Zilbermann

2

Cara mudah untuk mengetahui juga adalah men-debug kode Anda dan perhatikan kapan akhirnya dipanggil.


1

Menguji dengan Aplikasi Konsol C #, kode akhirnya telah dieksekusi setelah pengecualian dilemparkan: "Dialog Kesalahan Aplikasi" ada dan setelah Anda memilih opsi "Tutup program", blok akhirnya dieksekusi di jendela konsol itu. Tetapi pengaturan titik putus di dalam blok kode akhirnya, saya tidak pernah bisa memukulnya. Debugger terus berhenti pada pernyataan melempar. Ini kode pengujian saya:

    class Program
    {
       static void Main(string[] args)
       {
          string msg;
          Console.WriteLine(string.Format("GetRandomNuber returned: {0}{1}", GetRandomNumber(out msg), msg) == "" ? "" : "An error has occurred: " + msg);
       }

       static int GetRandomNumber(out string errorMessage)
       {
         int result = 0;
         try
         {
            errorMessage = "";
            int test = 0;
            result = 3/test;
            return result;
         }
         catch (Exception ex)
         {
            errorMessage = ex.Message;
            throw ex;

         }
         finally
         {
            Console.WriteLine("finally block!");
         }

       }
    }

Debugging di VS2010 - .NET Framework 4.0

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.