Sử dụng khối 'thử cuối cùng' mà không có khối 'bắt'


Câu trả lời:


120

Bạn sẽ sử dụng nó để đảm bảo một số hành động xảy ra sau trynội dung hoặc trên một ngoại lệ, nhưng khi bạn không muốn sử dụng ngoại lệ đó.

Chỉ cần rõ ràng, điều này không ẩn ngoại lệ. Các finallykhối được thực hiện trước các ngoại lệ được truyền lên các cuộc gọi stack.

Bạn cũng sẽ vô tình sử dụng nó khi bạn sử dụng usingtừ khóa, vì từ khóa này biên dịch thành một try-finally(không phải là một chuyển đổi chính xác, nhưng vì lợi ích của đối số, nó đủ gần).

try
{
    TrySomeCodeThatMightException();
}
finally
{
    CleanupEvenOnFailure();
}

Mã đang chạy trong finallykhông được đảm bảo sẽ chạy, tuy nhiên, trường hợp không được đảm bảo là khá phức tạp - tôi thậm chí không thể nhớ nó. Tất cả những gì tôi nhớ là, nếu bạn ở trong trường hợp đó, rất có thể việc không chạy finallykhông phải là vấn đề lớn nhất của bạn :-) vì vậy về cơ bản đừng đổ mồ hôi.

Cập nhật từ Tobias: finally sẽ không chạy nếu quá trình bị ngắt.

Cập nhật từ Paddy: Các điều kiện khi cuối cùng không thực thi trong một khối .net try..finally

Ví dụ phổ biến nhất mà bạn có thể thấy là loại bỏ kết nối cơ sở dữ liệu hoặc tài nguyên bên ngoài ngay cả khi mã không thành công:

using (var conn = new SqlConnection("")) // Ignore the fact we likely use ORM ;-)
{
    // Do stuff.
}

Biên dịch thành một cái gì đó như:

SqlConnection conn;

try
{
    conn = new SqlConnection("");
    // Do stuff.
}
finally
{
    if (conn != null)
        conn.Dispose();
}

14
@AnthonyBlake Ngoại lệ không bị ẩn. Nếu một ngoại lệ xảy ra, nó sẽ chạy cuối cùng và sau đó truyền ngoại lệ đó sao lưu ngăn xếp cuộc gọi.
Adam Houldsworth

2
Đối với trường hợp cạnh, cuối cùng sẽ không chạy nếu quá trình bị giết (ví dụ: với "kết thúc quá trình" trong trình quản lý tác vụ hoặc do mất điện, chẳng hạn).
Nuffin

1
Thông tin thêm ở đây về thời điểm một tuyên bố cuối cùng sẽ không kích hoạt - không chỉ khi quy trình bị hủy: stackoverflow.com/questions/111597/…
Paddy

1
Một trường hợp rõ ràng khác mà không phải tất cả các cuối cùng sẽ chạy là nếu một lỗi xảy ra ở giữa khối cuối cùng.
Ben Hocking

2
Cuối cùng sẽ không được gọi trong ba tình huống mà tôi có thể nghĩ đến: Diệt quy trình, tràn ngăn xếp và hết bộ nhớ. Tất cả những điều này về cơ bản sẽ ngừng thực thi chương trình tại điểm chính xác mà chúng gặp phải và báo hiệu hệ điều hành kết thúc quá trình.
KeithS

5

usinglà tương đương try-finally. Bạn sẽ chỉ sử dụng try-finallykhi muốn dọn dẹp bên trong finallyvà không quan tâm đến ngoại lệ.

Cách tiếp cận tốt nhất sẽ là

try
{
   using(resource)
   {
       //Do something here
   }   
}catch(Exception)
{
     //Handle Error
}

Làm như vậy ngay cả khi dọn dẹp được gọi bởi usingkhông thành công, mã của bạn sẽ không bị lỗi.

Có một số điều kiện khi nào finallysẽ không được thực thi.

  • Nếu có bất kỳ StackOverflowExceptionhoặc ExecutingEngineException.
  • Quá trình bị giết từ nguồn bên ngoài.

Hy vọng điều này giải đáp nghi ngờ của bạn.


3

Ví dụ: nếu bạn có tài nguyên không được quản lý mà bạn tạo và sử dụng trong khối thử, bạn có thể sử dụng khối cuối cùng để đảm bảo bạn giải phóng tài nguyên đó. Khối cuối cùng sẽ luôn được thực thi bất chấp điều gì xảy ra (ví dụ: ngoại lệ) trong khối try.

Ví dụ: câu lệnh lock (x) thực sự là:

System.Threading.Monitor.Enter(x); 
try { ... } 
finally 
{ 
    System.Threading.Monitor.Exit(x); 
} 

Khối cuối cùng sẽ luôn được gọi để đảm bảo khóa độc quyền được phát hành.


3

Giải thích tốt bằng cách sử dụng mã:

void MyMethod1()
{
    try
    {
        MyMethod2();
        MyMethod3();
    }
    catch(Exception e)
    {
        //do something with the exception
    }
}


void MyMethod2()
{
    try
    {
        //perform actions that need cleaning up
    }
    finally
    {
        //clean up
    }
}


void MyMethod3()
{
    //do something
}

Nếu MyMethod2 hoặc MyMethod3 ném một ngoại lệ, nó sẽ bị MyMethod1 bắt. Tuy nhiên, mã trong MyMethod2 cần chạy mã dọn dẹp, ví dụ: đóng kết nối cơ sở dữ liệu, trước khi ngoại lệ được chuyển cho MyMethod1.

http://forums.asp.net/t/1092267.aspx?Try+without+Catch+but+with+finally+doesn+t+throw+error+Why+no+syntax+error+


2
Ví dụ Ann về 'dọn dẹp' này có thể là saveLogFile (), mà tôi thường muốn đặt ở cuối chương trình bất kể điều gì.
Vincent

1

Bạn cần một khối cuối cùng, khi bất kể trường hợp ngoại lệ nào (nếu có) bị bắt hoặc thậm chí nếu không có khối nào bị bắt, bạn vẫn muốn thực thi một số mã trước khi khối thoát. Ví dụ, bạn có thể muốn đóng một tệp đang mở.

Xem Cũng thử cuối cùng


1

thử / cuối cùng: khi bạn không muốn xử lý bất kỳ ngoại lệ nào nhưng muốn đảm bảo (các) hành động xảy ra cho dù có hay không một ngoại lệ được đưa ra bởi mã được gọi.


1

Tôi không biết gì về C #, nhưng có vẻ như bất cứ điều gì bạn có thể làm với một lần thử, bạn có thể làm một cách thanh lịch hơn với một câu lệnh using . C ++ thậm chí không có cuối cùng là kết quả của RAII của nó .


Không cần thiết. Điều gì sẽ xảy ra nếu bạn muốn chạy một số mã tùy chỉnh không được xử lý bằng cách xử lý đơn giản. Ví dụ: nếu bạn tăng một bộ đếm trong try, sau đó giảm nó trong a finally, bạn không thể làm điều đó trong một usingcâu lệnh.
Mark A. Donohoe

@ MarkA.Donohoe Bạn có thể không tạo một đối tượng chứa một tham chiếu đến bộ đếm, đối tượng này sẽ giảm khi được xử lý?
Neil G

Có, nhưng tại sao lại tạo toàn bộ một đối tượng chỉ để triển khai giao diện dùng một lần để sử dụng với câu lệnh using? Điều đó phù hợp với vấn đề với giải pháp. A Try-[Catch]-Finallyxử lý tất cả những điều đó mà không cần phải tạo một đối tượng như vậy.
Mark A. Donohoe

@ MarkA.Donohoe Giải pháp đối tượng là ưu việt hơn vì nó thực hiện một mẫu bao bì ở một nơi. Bạn không thể quên, thất lạc hoặc vô tình xóa mã giảm dần.
Neil G

Tôi xin lỗi, nhưng tôi không đồng ý. Bạn đang tập trung vào hành vi giảm dần một cách rõ ràng, nhưng ai nói rằng chúng phải hoàn toàn phù hợp? Điều gì sẽ xảy ra nếu có logic tăng / giảm khác cũng phụ thuộc vào các thành viên khác trong hàm? Thêm vào đó, giờ đây bạn không chỉ giới thiệu một đối tượng mới chỉ cho một lần sử dụng, mà bạn đang làm như vậy trên heap thay vì một int đơn giản trên stack. Chắc chắn có thời gian và địa điểm để sử dụng usingnhưng bạn không thể thực hiện các tuyên bố chăn như bạn có. Một lần nữa, đó là xác định vấn đề bằng giải pháp, đối với tôi là ngược lại.
Mark A. Donohoe

1

Đây là một tình huống mà bạn có thể muốn sử dụng, hãy thử cuối cùng: khi bạn thường sử dụng câu lệnh using, nhưng không thể vì bạn đang gọi một phương thức bằng phản xạ.

Điều này sẽ không hoạt động

using (objMsg  =  Activator.CreateInstance(TypeAssist.GetTypeFromTypeName("omApp.MessagingBO")))
{

}

thay vào đó sử dụng

           object objMsg = null;
            try
            {
                objMsg
                   = Activator.CreateInstance(TypeAssist.GetTypeFromTypeName("myAssembly.objBO"));

                strResponse = (string)objMsg.GetType().InvokeMember("MyMethod", BindingFlags.Public
                        | BindingFlags.Instance | BindingFlags.InvokeMethod, null, objMsg,
                        new object[] { vxmlRequest.OuterXml });
            }               
            finally
            {
                if (objMsg!=null)
                    ((IDisposable)objMsg).Dispose();
            }


0

Đây là một trường hợp sử dụng mà tôi luôn (uhm ..) sử dụng:

int? x; //note the nullable type here!
try
{
    x = int.Parse(someString);
}
catch { } //don't care, let it just be null

0

1. chúng ta có thể sử dụng khối try mà không cần bắt nhưng chúng ta nên sử dụng khối bắt / cuối cùng, bất kỳ một trong số chúng. 2. chúng tôi không thể sử dụng chỉ thử khối.

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.