2009-03-19 23 views
144

Cái gì như:trở ở giữa một khối sử dụng

using (IDisposable disposable = GetSomeDisposable()) 
{ 
    //..... 
    //...... 
    return Stg(); 
} 

Tôi tin rằng nó không phải là nơi thích hợp cho một tuyên bố trở lại, phải không?

Trả lời

149

Như nhiều người khác đã chỉ ra nói chung đây không phải là vấn đề.

Trường hợp duy nhất nó sẽ gây ra cho bạn sự cố là nếu bạn quay trở lại ở giữa câu lệnh sử dụng và trả lại thêm biến đang sử dụng. Nhưng sau đó một lần nữa, điều này cũng sẽ gây ra cho bạn các vấn đề ngay cả khi bạn không trở lại và chỉ đơn giản là giữ một tham chiếu đến một biến.

using (var x = new Something()) { 
    // not a good idea 
    return x; 
} 

Cũng như xấu

Something y; 
using (var x = new Something()) { 
    y = x; 
} 
+1

Tôi vừa sửa câu hỏi của tôi về điểm mà bạn đã đề cập. Cảm ơn. – tafa

+0

Hãy giúp tôi hiểu tại sao điều này là xấu. Tôi muốn trả về một luồng mà tôi đang sử dụng trong một hàm trợ giúp cho một hàm khác để xử lý hình ảnh. Có vẻ như Stream sẽ được xử lý nếu tôi làm điều này? –

+1

@JohnShedletsky Trong trường hợp này, cuộc gọi chức năng của bạn nên được bao bọc bằng cách sử dụng. Giống như sử dụng (Stream x = FuncToReturnStream()) {...} và không sử dụng bên trong FuncToReturnStream. –

23

Điều này sẽ hoạt động hoàn toàn tốt, cũng giống như quay lại ở giữa try{}finally{}

14

Điều đó hoàn toàn có thể chấp nhận được. A sử dụng câu lệnh đảm bảo đối tượng IDisposable sẽ được xử lý bất kể là gì.

Từ MSDN:

Những tuyên bố sử dụng đảm bảo rằng Dispose được gọi là thậm chí nếu một ngoại lệ xảy ra trong khi bạn đang gọi phương pháp trên đối tượng. Bạn có thể đạt được kết quả tương tự bằng cách đặt đối tượng bên trong khối thử và sau đó gọi Vứt bỏ trong một khối cuối cùng; trên thực tế, đây là cách mà trình biên dịch sử dụng được dịch bởi trình biên dịch.

85

Hoàn toàn ổn - không sao cả. Tại sao bạn tin rằng nó sai?

Tuyên bố sử dụng chỉ là cú pháp cú pháp cho một khối thử/cuối cùng, và như Grzenio cho biết cũng tốt khi quay trở lại từ khối thử.

Biểu thức trả về sẽ được đánh giá, sau đó khối cuối cùng sẽ được thực hiện, sau đó phương thức sẽ trả về.

+5

Câu trả lời của James Curran giải thích những gì tôi đang nghĩ. – tafa

96

Nó hoàn toàn ổn.

Bạn đang dường như nghĩ rằng

using (IDisposable disposable = GetSomeDisposable()) 
{ 
    //..... 
    //...... 
    return Stg(); 
} 

được một cách mù quáng dịch sang:

IDisposable disposable = GetSomeDisposable() 
//..... 
//...... 
return Stg(); 
disposable.Dispose(); 

Trong đó, phải thừa nhận, sẽ là một vấn đề, và sẽ làm cho tuyên bố using khá vô nghĩa --- đó là tại sao đó là không phải những gì nó làm.

Trình biên dịch đảm bảo rằng đối tượng được xử lý trước khi điều khiển rời khỏi khối - bất kể nó rời khỏi khối như thế nào.

+5

Tôi đã, rõ ràng. – tafa

+0

Câu trả lời tuyệt vời @ James Curran! Nhưng nó làm cho tôi khá tò mò những gì nó được dịch sang. Hay chỉ có thể diễn tả trong IL? (mà tôi chưa bao giờ thực sự cố đọc trước đây). – Bart

+0

@Bart - Tôi nghĩ về nó khi đánh giá biểu thức trả về thành một biến tạm thời, sau đó thực hiện xử lý, sau đó trả về biến tạm thời. – ToolmakerSteve

-3

Có lẽ nó không phải là 100% sự thật rằng đây là chấp nhận được ...

Nếu bạn tình cờ được usings làm tổ và trở về từ bên trong một lồng nhau , nó có thể không an toàn.

Cầm lấy cái này làm ví dụ:

using (var memoryStream = new MemoryStream()) 
{ 
    using (var textwriter = new StreamWriter(memoryStream)) 
    { 
     using (var csv = new CsvWriter(textwriter)) 
     { 
      //..write some stuff to the stream using the CsvWriter 
      return memoryStream.ToArray(); 
     } 
    } 
} 

Tôi đã đi qua trong một DataTable được outputted như csv. Với sự trở lại ở giữa, nó đã được viết tất cả các hàng để dòng, nhưng csv xuất đã luôn luôn thiếu một hàng (hoặc nhiều, tùy thuộc vào kích thước của bộ đệm). Điều này nói với tôi rằng một cái gì đó đã không được đóng đúng cách.

Cách đúng là để đảm bảo tất cả các usings trước đó được xử lý đúng cách:

using (var memoryStream = new MemoryStream()) 
{ 
    using (var textwriter = new StreamWriter(memoryStream)) 
    { 
     using (var csv = new CsvWriter(textwriter)) 
     { 
      //..write some stuff to the stream using the CsvWriter 
     } 
    } 

    return memoryStream.ToArray(); 
} 
1

Đoạn mã bên dưới cho thấy cách using đang làm việc:

private class TestClass : IDisposable 
{ 
    private readonly string id; 

    public TestClass(string id) 
    { 
     Console.WriteLine("'{0}' is created.", id); 
     this.id = id; 
    } 

    public void Dispose() 
    { 
     Console.WriteLine("'{0}' is disposed.", id); 
    } 

    public override string ToString() 
    { 
     return id; 
    } 
} 

private static TestClass TestUsingClose() 
{ 
    using (var t1 = new TestClass("t1")) 
    { 
     using (var t2 = new TestClass("t2")) 
     { 
     using (var t3 = new TestClass("t3")) 
     { 
      return new TestClass(String.Format("Created from {0}, {1}, {2}", t1, t2, t3)); 
     } 
     } 
    } 
} 

[TestMethod] 
public void Test() 
{ 
    Assert.AreEqual("Created from t1, t2, t3", TestUsingClose().ToString()); 
} 

Output:

't1' được tạo.
't2' được tạo.
't3' được tạo.
'Được tạo từ t1, t2, t3' được tạo.
't3' được xử lý.
't2' được xử lý.
't1' được xử lý.

Xử lý được gọi sau câu lệnh trả về nhưng trước khi thoát khỏi hàm.

Các vấn đề liên quan