thử / bắt + sử dụng, cú pháp đúng


189

Cái nào:

using (var myObject = new MyClass())
{
   try
   {
      // something here...
   }
   catch(Exception ex)
   {
      // Handle exception
   }
}

HOẶC LÀ

try
{
   using (var myObject = new MyClass())
   {
      // something here...
   }
}
catch(Exception ex)
{
   // Handle exception
}

7
Chỉ cần một lưu ý: người ta nên cẩn thận chỉ bắt những ngoại lệ thực sự có thể được xử lý (sửa chữa), ngoại trừ việc đăng nhập hoặc gói chúng.
John Saunders

1
Xin lưu ý rằng cuối cùng }của usingtuyên bố có thể đưa ra một ngoại lệ như được nhắc ở đây .
Giulio Caccin

1
TIL rằng trình gỡ lỗi (trong VS) sẽ không gọi phương thức xử lý nếu bạn sử dụng khối mã đầu tiên. Bởi vì chính câu lệnh sử dụng có thể đưa ra một ngoại lệ, nó giúp tôi sử dụng khối thứ hai để đảm bảo hàm ý finallyđược gọi là phương thức xử lý.
ShooShoSha

Câu trả lời:


98

Tôi thích cái thứ hai. Cũng có thể bẫy các lỗi liên quan đến việc tạo ra các đối tượng là tốt.


11
Tôi không đồng ý với lời khuyên này. Nếu bạn đang mong đợi việc tạo đối tượng gây ra lỗi, thì mọi xử lý ngoại lệ đó phải được đưa ra ngoài. Nếu có một số câu hỏi về việc xử lý nên đi đâu, thì ngoại lệ được dự kiến ​​phải là một thứ gì đó khác trừ khi bạn chủ trương bắt bất kỳ ngoại lệ ngẫu nhiên nào có thể hoặc không thể lường trước được, đó là một kiểu chống cổ điển (bên ngoài một xử lý hoặc xử lý ngoại lệ chưa xử lý của chủ đề).
Jeffrey L Whitledge

1
@Jeffrey: Cách tiếp cận tôi mô tả đã phục vụ tốt cho tôi và tôi đã làm điều này từ lâu. Không ai nói bất cứ điều gì về việc mong đợi việc tạo đối tượng sẽ thất bại. Nhưng bằng cách gói một hoạt động có khả năng thất bại trong một trykhối, cho phép bạn bật một thông báo lỗi nếu có lỗi, chương trình hiện có khả năng khôi phục và thông báo cho người dùng.
Jonathan Wood

Câu trả lời của bạn là đúng nhưng vẫn tiếp tục đề nghị rằng try / catch được ở đó (ngay lập tức) bất cứ lúc nào.
Henk Holterman

17
Tôi nghĩ rằng người đầu tiên cũng có công, hãy xem xét một giao dịch DB using( DBConnection conn = DBFactory.getConnection())cần được khôi phục trong trường hợp có ngoại lệ xảy ra. Dường như với tôi rằng cả hai đều có vị trí của họ.
wfoster

1
Điều đó cũng sẽ bẫy các lỗi liên quan đến việc xử lý đối tượng.
Ahmad Ibrahim

39

Vì một khối sử dụng chỉ là một đơn giản hóa cú pháp của một lần thử / cuối cùng ( MSDN ), cá nhân tôi sẽ đi theo cách sau, mặc dù tôi nghi ngờ nó khác biệt đáng kể so với tùy chọn thứ hai của bạn:

MyClass myObject = null;
try {
  myObject = new MyClass();
  //important stuff
} catch (Exception ex) {
  //handle exception
} finally {
  if(myObject is IDisposable) myObject.Dispose();
}

4
Tại sao bạn nghĩ rằng thêm một finallykhối là thích hợp hơn cho usingtuyên bố?
Cody Grey

10
Thêm một finallykhối xử lý một đối tượng IDis Dùng là những gì một usingcâu lệnh làm. Cá nhân, tôi thích điều này thay vì usingkhối nhúng vì tôi nghĩ rằng nó rõ ràng hơn trong đó mọi thứ đang diễn ra, và đó là tất cả trên cùng một "cấp độ". Tôi cũng thích điều này hơn một số usingkhối nhúng ... nhưng tất cả chỉ là sở thích của tôi.
chezy525

8
Nếu bạn thực hiện nhiều xử lý ngoại lệ, bạn phải thực sự thích gõ! Từ khóa "sử dụng" đó đã xuất hiện được một thời gian và ý nghĩa của nó khá rõ ràng đối với tôi. Và sử dụng nó giúp làm cho phần còn lại của mã của tôi rõ ràng hơn bằng cách giữ mức độ lộn xộn ở mức tối thiểu.
Jonathan Wood

2
Điều này là không đúng. Đối tượng phải được khởi tạo bên ngoài trycâu lệnh để nó được xử lý trong finallycâu lệnh; nếu không, nó sẽ đưa ra một lỗi trình biên dịch: "Sử dụng biến cục bộ chưa được gán 'myObject'"
Steve Konves

3
Về mặt kỹ thuật, điều đó sẽ không được biên dịch. Cannot assign null to implicitly-typed local variable;) Nhưng tôi biết ý của bạn là gì và cá nhân tôi thích điều này hơn khi lồng một khối sử dụng.
Connell

20

Nó phụ thuộc. Nếu bạn đang sử dụng Windows Communication Foundation (WCF), using(...) { try... }sẽ không hoạt động chính xác nếu proxy trong usingcâu lệnh ở trạng thái ngoại lệ, tức là việc loại bỏ proxy này sẽ gây ra ngoại lệ khác.

Cá nhân, tôi tin vào phương pháp xử lý tối thiểu, tức là chỉ xử lý ngoại lệ mà bạn biết ở điểm thực thi. Nói cách khác, nếu bạn biết rằng việc khởi tạo một biến trong usingcó thể đưa ra một ngoại lệ cụ thể, tôi sẽ bọc nó lại try-catch. Tương tự, nếu trong usingcơ thể một cái gì đó có thể xảy ra, không liên quan trực tiếp đến biến số using, thì tôi bọc nó với một cái khác trycho ngoại lệ cụ thể đó. Tôi hiếm khi sử dụng Exceptiontrong catches của tôi .

Nhưng tôi thích IDisposableusingmặc dù vậy tôi có thể thiên vị.


19

Nếu câu lệnh bắt của bạn cần truy cập vào biến được khai báo trong câu lệnh sử dụng, thì bên trong là tùy chọn duy nhất của bạn.

Nếu câu lệnh bắt của bạn cần đối tượng được tham chiếu trong quá trình sử dụng trước khi nó được xử lý, thì bên trong là tùy chọn duy nhất của bạn.

Nếu câu lệnh bắt của bạn thực hiện một hành động có thời lượng không xác định, như hiển thị thông báo cho người dùng và bạn muốn loại bỏ tài nguyên của mình trước khi điều đó xảy ra, thì bên ngoài là lựa chọn tốt nhất của bạn.

Bất cứ khi nào tôi có một scenerio tương tự như thế này, khối thử bắt thường ở một phương thức khác để tăng thêm ngăn xếp cuộc gọi từ việc sử dụng. Nó không phải là điển hình cho một phương pháp để biết cách xử lý các trường hợp ngoại lệ xảy ra trong nó như thế này.

Vì vậy, giới thiệu chung của tôi là bên ngoài cách thức bên ngoài.

private void saveButton_Click(object sender, EventArgs args)
{
    try
    {
        SaveFile(myFile); // The using statement will appear somewhere in here.
    }
    catch (IOException ex)
    {
        MessageBox.Show(ex.Message);
    }
}

10

Cả hai đều là cú pháp hợp lệ. Nó thực sự phụ thuộc vào những gì bạn muốn làm: nếu bạn muốn bắt lỗi liên quan đến việc tạo / xử lý đối tượng, hãy sử dụng cái thứ hai. Nếu không, sử dụng đầu tiên.


8

Có một điều quan trọng mà tôi sẽ gọi ra ở đây: Điều đầu tiên sẽ không bắt gặp bất kỳ ngoại lệ nào phát sinh từ việc gọi hàm MyClasstạo.


3

Từ C # 8.0, tôi thích sử dụng cái thứ hai giống như thế này

public class Person : IDisposable
{
    public Person()
    {
        int a = 0;
        int b = Id / a;
    }
    public int Id { get; set; }

    public void Dispose()
    {
    }
}

và sau đó

static void Main(string[] args)
    {

        try
        {
            using var person = new Person();
        }
        catch (Exception ex) when
        (ex.TargetSite.DeclaringType.Name == nameof(Person) &&
        ex.TargetSite.MemberType == System.Reflection.MemberTypes.Constructor)
        {
            Debug.Write("Error Constructor Person");
        }
        catch (Exception ex) when
       (ex.TargetSite.DeclaringType.Name == nameof(Person) &&
       ex.TargetSite.MemberType != System.Reflection.MemberTypes.Constructor)
        {
            Debug.Write("Error Person");
        }
        catch (Exception ex)
        {
            Debug.Write(ex.Message);
        }
        finally
        {
            Debug.Write("finally");
        }
    }

1

Nếu đối tượng bạn đang khởi tạo trong khối Sử dụng () có thể đưa ra bất kỳ ngoại lệ nào thì bạn nên sử dụng cú pháp thứ hai nếu không cả hai đều hợp lệ như nhau.

Trong kịch bản của tôi, tôi đã phải mở một tệp và tôi đã truyền tệpPath trong hàm tạo của đối tượng mà tôi đang khởi tạo trong khối Sử dụng () và nó có thể ném ngoại lệ nếu tệpPath sai / trống. Vì vậy, trong trường hợp này, cú pháp thứ hai có ý nghĩa.

Mã mẫu của tôi: -

try
{
    using (var obj= new MyClass("fileName.extension"))
    {

    }
}
catch(Exception ex)
{
     //Take actions according to the exception.
}

1

Từ C # 8.0 trở đi , bạn có thể đơn giản hóa các usingcâu lệnh trong một số điều kiện để thoát khỏi khối lồng nhau, và sau đó nó chỉ áp dụng cho khối kèm theo.

Vì vậy, hai ví dụ của bạn có thể được giảm xuống thành:

using var myObject = new MyClass();
try
{
   // something here...
}
catch(Exception ex)
{
   // Handle exception
}

Và:

try
{
   using var myObject = new MyClass();
   // something here...
}
catch(Exception ex)
{
   // Handle exception
}

Cả hai đều khá rõ ràng; và sau đó điều đó làm giảm sự lựa chọn giữa hai vấn đề về vấn đề bạn muốn phạm vi của đối tượng là gì, nơi bạn muốn xử lý các lỗi khởi tạo và khi nào bạn muốn loại bỏ nó.

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.