Tại sao cuối cùng sử dụng trong C #?


187

Bất cứ thứ gì bên trong cuối cùng cũng được thực thi (hầu như) luôn luôn, vậy sự khác biệt giữa việc đặt mã vào nó hoặc không được tiết lộ là gì?


3
Bạn có ý nghĩa gì khi để nó không được tiết lộ?
Ramesh

5
Và ý của bạn là "(gần như)" là gì?
Beska

49
Nếu bạn kéo dây nguồn ra trong khi máy đang thực hiện mệnh đề thử thì mệnh đề cuối cùng sẽ không được gọi.
Dour High Arch

5
lol, vâng, điều đó đúng, nhưng bạn thực sự không thể viết mã cho điều đó được không?
Ed S.

2
@Ed: Sử dụng giao dịch. Mệnh đề thử của bạn phải thực hiện một số loại thay đổi tạm thời hoặc trong bộ nhớ có thể được duy trì trong một thay đổi duy nhất, nguyên tử, trong mệnh đề cuối cùng. Điều này hiếm khi dễ dàng và có thể yêu cầu phần cứng đặc biệt.
Dour High Arch

Câu trả lời:


403

Mã bên trong một khối cuối cùng sẽ được thực thi bất kể có hay không có ngoại lệ. Điều này rất hữu ích khi nói đến một số chức năng vệ sinh nhất định mà bạn cần luôn luôn chạy như đóng các kết nối.

Bây giờ, tôi đoán câu hỏi của bạn là tại sao bạn nên làm điều này:

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

Khi bạn có thể làm điều này:

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

alwaysDoThis();

Câu trả lời là rất nhiều lần mã bên trong câu lệnh bắt của bạn sẽ lấy lại một ngoại lệ hoặc thoát ra khỏi hàm hiện tại. Với mã sau, "alwaysDo This ();" cuộc gọi sẽ không được thực thi nếu mã bên trong câu lệnh bắt gặp trả lại hoặc ném ngoại lệ mới.


3
Hừm. Rất giống với những gì tôi nói, nhưng rõ ràng và chính xác hơn. Xác định +1.
Beska

45
điều này cũng áp dụng cho "return" bên trong khối thử {}.
Lucas

4
thực tế, nó áp dụng ngay cả khi không có khối {} (chỉ cần thử / cuối cùng, để ngoại lệ nổi lên)
Lucas

tốt hơn là của tôi, đã ở nơi làm việc và không muốn dành mười phút để trả lời chi tiết. +1
Matt Briggs

2
Vâng, đây chính xác là những gì tôi đã nghĩ: D Bây giờ tôi đã hiểu nó.
Rodrigo

62

Hầu hết các lợi ích của việc sử dụng thử - cuối cùng đã được chỉ ra, nhưng tôi nghĩ tôi đã thêm cái này:

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).
}

Hành vi này làm cho nó rất hữu ích trong các tình huống khác nhau, đặc biệt là khi bạn cần thực hiện dọn dẹp (xử lý tài nguyên), mặc dù một khối sử dụng thường tốt hơn trong trường hợp này.


2
Đây thực sự là lý do duy nhất tôi sử dụng cuối cùng
Christopher Townsend

12

bất cứ khi nào bạn sử dụng các yêu cầu mã không được quản lý như trình đọc luồng, yêu cầu db, v.v; và bạn muốn bắt ngoại lệ, sau đó sử dụng thử bắt cuối cùng và đóng luồng, trình đọc dữ liệu, v.v ... cuối cùng, nếu bạn không gặp lỗi khi kết nối không bị đóng, điều này thực sự tồi tệ với các yêu cầu 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();
        }

nếu bạn không muốn bắt lỗi thì hãy sử dụng

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

và đối tượng kết nối sẽ được xử lý tự động nếu có lỗi, nhưng bạn không nắm bắt được lỗi


2
Vứt bỏ () cũng sẽ Đóng () kết nối, không cần gọi cả hai. Đóng () KHÔNG Dipose (), bạn có thể mở lại kết nối.
Lucas

Đẹp, cảm ơn đã đề cập đến việc sử dụng. Tôi sẽ phải trả lời, nếu không.
Dan Rosenstark

10

Bởi vì cuối cùng sẽ được thực thi ngay cả khi bạn không xử lý một ngoại lệ trong khối bắt.


7

Cuối cùng các câu lệnh có thể thực thi ngay cả sau khi trả về.

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
}

7

finally, như trong:

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

là một cơ hội được đảm bảo để thực thi mã sau try..catch khối , bất kể khối thử của bạn có ném ngoại lệ hay không.

Điều đó làm cho nó hoàn hảo cho những thứ như giải phóng tài nguyên, kết nối db, xử lý tệp, v.v.


3
Tất cả các ví dụ đó thường được phục vụ tốt hơn với một khối sử dụng, nhưng điều đó không thực sự làm mất đi câu trả lời của bạn.
Joel Coehoorn

4

tôi sẽ giải thích việc sử dụng cuối cùng với một ngoại lệ đọc tập tin Ví dụ

  • cuối cùng không sử dụng
try{

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

trong ví dụ trên nếu tệp có tên Data.txt bị thiếu, một ngoại lệ sẽ được ném và sẽ được xử lý nhưng câu lệnh được gọi StreamReader.Close();sẽ không bao giờ được thực thi.
Bởi vì tài nguyên này liên quan đến độc giả không bao giờ được phát hành.

  • Để giải quyết vấn đề trên, cuối cùng chúng ta cũng sử dụng
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();
    }
}

Chúc mừng mã hóa :)

Lưu ý: "@" được sử dụng để tạo một chuỗi nguyên văn , để tránh lỗi "Trình tự thoát không được nhận dạng". Biểu tượng @ có nghĩa là đọc chuỗi đó theo nghĩa đen và không diễn giải các ký tự điều khiển khác.


2

Giả sử bạn cần đặt con trỏ trở về con trỏ mặc định thay vì con trỏ chờ (đồng hồ cát). Nếu một ngoại lệ được ném ra trước khi đặt con trỏ và không làm hỏng ứng dụng, bạn có thể bị bỏ lại với một con trỏ khó hiểu.


2

Đôi khi bạn không muốn xử lý một ngoại lệ (không có khối bắt), nhưng bạn muốn một số mã dọn dẹp thực thi.

Ví dụ:

try
{
    // exception (or not)
}
finally
{
    // clean up always
}

Nếu ngoại lệ không bị bắt, việc thực thi khối cuối cùng phụ thuộc vào việc hệ điều hành có chọn kích hoạt hoạt động thư giãn ngoại lệ hay không.
Vikas Verma

2

Khối cuối cùng có giá trị để dọn sạch mọi tài nguyên được phân bổ trong khối thử cũng như chạy bất kỳ mã nào phải thực thi ngay cả khi có ngoại lệ. Kiểm soát luôn được chuyển đến khối cuối cùng bất kể khối thoát thử như thế nào.


1

À ... tôi nghĩ tôi thấy những gì bạn đang nói! Mất tôi một giây ... bạn đang tự hỏi "tại sao lại đặt nó vào khối cuối cùng thay vì sau khối cuối cùng và hoàn toàn nằm ngoài thử-bắt-cuối".

Ví dụ, có thể là do bạn đang tạm dừng thực thi nếu bạn gặp lỗi, nhưng bạn vẫn muốn dọn sạch các tài nguyên, chẳng hạn như mở tệp, kết nối cơ sở dữ liệu, v.v.


1

Luồng điều khiển của Khối cuối cùng là sau khối Thử hoặc Bắt.

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

với Ngoại lệ 1> 2> 3> 4> 5 nếu 3 có câu lệnh Trả về 1> 2> 3> 4

không có ngoại lệ 1> 2> 4> 5 nếu 2 có câu lệnh return 1> 2> 4


0

Như đã đề cập trong tài liệu :

Một cách sử dụng phổ biến của việc bắt và cuối cùng là cùng nhau để có được và sử dụng tài nguyên trong khối thử, xử lý các trường hợp đặc biệt trong khối bắt và giải phóng tài nguyên trong khối cuối cùng.

Nó cũng đáng đọc điều này , trong đó nêu rõ:

Khi một mệnh đề bắt phù hợp được tìm thấy, hệ thống chuẩn bị chuyển điều khiển sang câu lệnh đầu tiên của mệnh đề bắt. Trước khi thực hiện mệnh đề bắt đầu, hệ thống trước tiên thực hiện, theo thứ tự, bất kỳ mệnh đề cuối cùng nào được liên kết với các câu lệnh thử được lồng nhiều hơn so với mệnh đề bắt ngoại lệ.

Vì vậy, rõ ràng mã nằm trong finallymệnh đề sẽ được thực thi ngay cả khi catchmệnh đề trước có returncâu lệnh.

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.