Khi nào cuối cùng được chạy nếu bạn ném một ngoại lệ từ khối bắt?


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

Trong khối trên, khối cuối cùng được gọi là khi nào? Trước khi ném e hay cuối cùng cũng được gọi rồi bắt?


14
ps bạn không nên "ném e;" bởi vì điều đó sẽ làm rối tung dấu vết ngăn xếp của ngoại lệ ban đầu. Bạn chỉ nên "ném;". Hoặc tạo một ngoại lệ mới và đặt InternalException thành "e" trước khi bạn ném nó.
Erv Walter

24
cuối cùng sẽ là một lựa chọn từ khóa khá kém nếu nó không chạy lần cuối , bạn có nói không?
Eric Lippert

@ErvWalter điều này có còn đúng không? Tôi đang thử nghiệm cả hai cách trong VS2017, và nó dường như giống hệt nhau. Bạn có thể cung cấp thêm một số thông tin hoặc một tài liệu tham khảo? Cảm ơn
Jeff Puckett

chỉ cần đặt tên đề xuất sử dụng Exception ex - reserved e cho các sự kiện / đại biểu
mr R

Câu trả lời:


137

Nó sẽ được gọi sau khi e được ném lại (tức là sau khi khối bắt được thực thi)

chỉnh sửa 7 năm sau - một lưu ý quan trọng là nếu ekhông bị chặn bởi một khối thử / bắt tiếp tục lên ngăn xếp cuộc gọi hoặc được xử lý bởi một trình xử lý ngoại lệ toàn cầu, thì finallykhối đó có thể không bao giờ thực hiện được.


18
và không bao giờ nếu bạn gọi Envrionment.FailFast ()
Johannes Rudolph

16
Sau khi thử các mã trong câu trả lời của Brandon, tôi thấy rằng finallylà không thực thi nếu ngoại lệ ném vào trước catchkhông bao giờ bắt gặp trong một bên ngoài try- catchkhối!
Andrew

3
Cảm ơn bạn đã chỉnh sửa câu trả lời (được chấp nhận) của bạn để bao gồm thông tin mới.
Gordon Bean

3
Họ (Microsoft) nói về vấn đề này trên trang tài liệu mới: docs.microsoft.com/en-us/dotnet/csharp/lingu-reference/ trộm : "Trong một ngoại lệ được xử lý, khối cuối cùng được liên kết được đảm bảo sẽ được chạy. Tuy nhiên , nếu ngoại lệ được xử lý, việc thực thi khối cuối cùng phụ thuộc vào cách kích hoạt hoạt động thư giãn ngoại lệ. Điều đó, đến lượt nó, phụ thuộc vào cách máy tính của bạn được thiết lập. "
DotNetSparky

1
Lưu ý rằng "bị bắt bởi một khối thử / bắt tiếp tục trong ngăn xếp cuộc gọi" sẽ bao gồm các trình xử lý khung như những người trong ASP.NET hoặc một người chạy thử. Một cách tốt hơn để đặt nó có thể là "nếu chương trình của bạn tiếp tục chạy sau khối bắt, thì khối cuối cùng sẽ thực thi."
ArrowCase

90

Tại sao không thử nó:

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

với mã (được định dạng cho không gian dọc):

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, đối với một cái gì đó đơn giản, bạn thực sự nên thử nó như Marc có. GJ minh họa nó với thử / bắt / lồng nhau cuối cùng :)
Allen Rice

1
@ ALLenRice tại sao thử nó nếu Marc đã có, và tôi chỉ có thể google để tìm câu trả lời của Marc? Hoặc có thể tốt hơn, hãy tự thử, sau đó tạo một câu hỏi SO và tự trả lời nó vì lợi ích của người khác.
joshden

8
Xin lưu ý rằng nếu bạn không bắt được ngoại lệ trong lần bắt bên ngoài, cuối cùng bên trong KHÔNG BAO GIỜ được thực thi !! Trong trường hợp đó, đầu ra làouter try inner try inner catch Unhandled Exception: System.DivideByZeroException...
Andrew

1
@Andrew bạn nói đúng. Giải thích bạn có thể tìm thấy ở đây Tạp chí MSDN 2008 Tháng 9: Xử lý ngoại lệ chưa được xử lý trong CLR (để mở chm cần mở khóa: Thuộc tính tệp -> Chung -> Mở khóa). Nếu bạn thay thế khối bắt bên ngoài bằng "bắt (ArgumentException)" thì cuối cùng cũng không có ai bị chặn, vì CLR không thể tìm thấy bất kỳ "trình xử lý ngoại lệ nào đồng ý xử lý ngoại lệ" DivideByZeroException.
vladimir

35

Sau khi đọc tất cả các câu trả lời ở đây, có vẻ như câu trả lời cuối cùng là tùy thuộc vào :

  • Nếu bạn ném lại một ngoại lệ trong khối bắt và ngoại lệ đó được bắt vào bên trong một khối bắt khác, mọi thứ sẽ thực thi theo tài liệu.

  • Tuy nhiên, nếu ngoại lệ trown lại được xử lý, cuối cùng không bao giờ thực thi.

Tôi đã thử nghiệm mẫu mã này trong 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();
    }

Đây là đầu ra:

Ví dụ 1: ném lại bên trong một khối thử khác:
- thử thử
---- thử
bên trong
---- bắt bên trong ---- cuối cùng bên trong -
bắt
đồ - cuối cùng là
Huzzah!

Ví dụ 2: ném lại bên ngoài khối thử khác:
--try
--catch

Ngoại lệ chưa được xử lý: System.Exception: Ngoại lệ của loại 'System.Exception' đã bị ném.
tại ConsoleApplication1.Program.Main () trong C: \ local source \ ConsoleApplication1 \ Program.cs: dòng 53


3
Tuyệt vời, tôi đã không nhận ra điều đó!
Andrew

1
Lưu ý rằng cuối cùng cuối cùng có thể chạy, tùy thuộc vào những gì bạn chọn: stackoverflow.com/a/46267841/480982
Thomas Weller

1
Thật thú vị ... Trên .NET Core 2.0, phần cuối cùng chạy sau ngoại lệ chưa được xử lý.
Mahdi Ghiasi

Thật thú vị, tôi vừa thực hiện một thử nghiệm trong cả .NET Core 2.0 và .NET Framework 4.6.1 và cả hai đều chạy cuối cùng sau ngoại lệ chưa được xử lý. Hành vi này đã thay đổi?
Cameron Bielstein

24

Ví dụ của bạn sẽ hành xử giống hệt với mã này:

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

Như một lưu ý phụ, nếu bạn thực sự có ý nghĩa throw e;( nghĩa là ném ngoại lệ giống như bạn vừa bắt), sẽ tốt hơn nhiều nếu chỉ throw;giữ lại dấu vết ngăn xếp ban đầu thay vì tạo một dấu vết mới.


Tôi không nghĩ rằng điều này là chính xác. Cuối cùng nên ở bên trong khối thử bên ngoài chứ không phải bên ngoài nó
Matthew Pigram

@MatthewPigram: Ý bạn là gì? Các finallykhối trong thực tế sẽ chạy sau khi catchkhối (ngay cả khi khối catch rethrows ngoại trừ), đó là những gì đoạn tôi đang cố gắng để minh họa.
Daniel Pryden

từ cách tôi diễn giải ví dụ của anh ấy, anh ấy đang cố gắng thực hiện một thử bắt cuối cùng trong một khối thử khác. KHÔNG phải là một thử bắt trong một lần thử cuối cùng
Matthew Pigram

1
@MatthewPigram: Câu trả lời của tôi không có bất kỳ cấu trúc "thử-bắt-cuối" nào cả. Nó có một "thử cuối cùng", và bên trong trykhối câu trả lời của tôi có một "thử bắt". Tôi đang cố gắng giải thích hành vi của cấu trúc 3 phần bằng cách sử dụng hai cấu trúc 2 phần. Tôi không thấy bất kỳ dấu hiệu nào của trykhối thứ hai trong câu hỏi ban đầu, vì vậy tôi không hiểu bạn đang lấy nó ở đâu.
Daniel Pryden

12

Nếu có một ngoại lệ chưa được xử lý trong khối xử lý bắt, khối cuối cùng được gọi chính xác là 0 lần

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

Đầu ra:

C: \ users \ Administrator \ Documents \ TestExceptionNesting \ bin \ Release> TestExceptionNesting.exe

trong thử

trong cái bẫy

Ngoại lệ chưa được xử lý: System.DivideByZeroException: Đã cố chia cho số không. tại TestExceptionNesting.Program.Main (String [] args) trong C: \ users \ Administrator \ Documents \ TestExceptionNesting \ TestExceptionNesting.cs: dòng 22

C: \ users \ Administrator \ Documents \ TestExceptionNesting \ bin \ release>

Tôi đã được hỏi câu hỏi này ngày hôm nay tại một cuộc phỏng vấn và người phỏng vấn tiếp tục quay lại "bạn có chắc chắn cuối cùng không được gọi không?" Tôi không chắc đó có phải là một câu hỏi mẹo hay người phỏng vấn có ý định gì khác và đã viết mã sai để tôi gỡ lỗi nên tôi về nhà và thử nó (xây dựng và chạy, không tương tác với trình gỡ lỗi), chỉ để tôi suy nghĩ nghỉ ngơi.


Trừ khi ngoại lệ bị ném bị bắt trong một lần bắt khác ở đâu đó cao hơn ngăn xếp, trong trường hợp đó, nó có thể thực thi nếu ngoại lệ ném đó được xử lý ... Hoặc tôi có thể sai ...
tomosius

@tomosius, vâng, đó là những gì câu trả lời của Brandon giải thích. :)
Andrew

@tomosius Đó là lý do tại sao tôi bắt đầu bằng cách chỉ định "Nếu có ngoại lệ chưa được xử lý". Nếu ngoại lệ ném được bắt gặp ở đâu đó thì theo định nghĩa, chúng ta đang nói về một trường hợp khác.
Eusebio Rufian-Zilbermann

Đây không phải là sự thật. Ít nhất là không cho NET Core 3.1. Một dự án giao diện điều khiển mới đơn giản với mã này hiển thị "cuối cùng" sau ngoại lệ.
emzero

Điều thú vị là hành vi đã thay đổi, lõi .NET thậm chí không tồn tại khi tôi đăng;)
Eusebio Rufian-Zilbermann

2

Một cách đơn giản để nói cũng là gỡ lỗi mã của bạn và thông báo khi cuối cùng được gọi.


1

Thử nghiệm với Ứng dụng Bảng điều khiển C #, mã cuối cùng đã được thực thi sau khi ném ngoại lệ: "Hộp thoại Lỗi ứng dụng" tồn tại và sau khi bạn chọn tùy chọn "Đóng chương trình", khối cuối cùng đã được thực thi trong cửa sổ bảng điều khiển đó. Nhưng thiết lập điểm phá vỡ bên trong khối mã cuối cùng, tôi không bao giờ có thể đánh nó. Trình gỡ lỗi tiếp tục dừng lại ở tuyên bố ném. Đây là mã kiểm tra của tôi:

    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!");
         }

       }
    }

Gỡ lỗi trong VS2010 - .NET Framework 4.0

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.