2010-04-20 45 views
5

Nếu tôi có mã với các đối tượng lồng nhau như thế này, tôi cần sử dụng các câu lệnh lồng nhau để đảm bảo rằng cả đối tượng SQLCommand và SQLConnection đều được xử lý đúng như được hiển thị bên dưới hoặc tôi ổn nếu mã mà instantiates SQLCommand nằm trong câu lệnh using outside.Sử dụng cách xử lý các đối tượng lồng nhau

using (var conn = new SqlConnection(sqlConnString)) 
{ 
    using (var cmd = new SqlCommand()) 
    { 
     cmd.CommandType = CommandType.Text; 
     cmd.CommandText = cmdTextHere; 
     conn.Open(); 

     cmd.Connection = conn; 
     rowsAffected = cmd.ExecuteNonQuery(); 
    } 
} 

Trả lời

8

Có. Bạn có thể làm sạch nó một chút như thế này

using (SqlConnection conn = new SqlConnection(sqlConnString)) 
using (System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand()) 
{ 
    // code here 
} 

Nhưng bạn vẫn muốn một using cho mỗi đối tượng IDisposable.

Edit: Hãy xem xét ví dụ này của không sử dụng một using tuyên bố bên trong.

class A : IDisposable 
{ 
    public void Dispose() 
    { 
     Console.WriteLine("A Disposed"); 
    } 
} 

class B : IDisposable 
{ 
    public void Dispose() 
    { 
     Console.WriteLine("B Disposed"); 
    } 
} 

using (A a = new A())    
{ 
    B b = new B(); 
} 

Vào cuối của khối A được xử lý đúng cách. Điều gì xảy ra với B? Vâng, nó nằm ngoài phạm vi và chỉ đơn giản là chờ đợi để được thu gom rác thải. B.Dispose() không được gọi. Mặt khác

using (A a = new A()) 
using (B b = new B()) 
{ 
} 

Khi thực hiện rời khỏi khối (trên thực tế, khối), mã biên dịch thực hiện cuộc gọi đến các phương thức Dispose() của từng đối tượng.

+0

Bất kỳ lý do cụ thể nào khiến nó luôn rõ ràng? Câu trả lời này lệch khỏi mục đích thực sự của câu hỏi. – Nayan

+0

@Nayan, hãy xem nhận xét của tôi cho câu trả lời của bạn. –

+0

Caveat: đừng làm điều này cho các lớp Stream khi họ sở hữu các luồng đã truyền! Xem http://stackoverflow.com/questions/1065168/does-disposing-streamreader-close-the-stream – fmuecke

4

Bạn nên gọi vứt bỏ trên cả hai, tuy nhiên, nó là dễ dàng hơn để đọc nếu bạn chỉ làm điều này:

using (SqlConnection conn = new SqlConnection(sqlConnString)) 
using (SqlCommand cmd = new SqlCommand()) 
{ 
    cmd.CommandType = CommandType.Text; 
    cmd.CommandText = cmdTextHere; 
    conn.Open(); 

    cmd.Connection = conn; 
    rowsAffected = cmd.ExecuteNonQuery(); 
} 

kể từ khi có một khối tiềm ẩn tạo ra (như một câu lệnh if hoặc tuyên bố) và kể từ khi sử dụng là toàn bộ một khối, bạn không cần niềng răng và nó trông neater trong quan điểm của tôi. Một số sẽ nói bạn shoul luôn luôn sử dụng niềng răng vì nó quá dễ dàng để vô tình thêm một tuyên bố thứ hai và tạo ra một lỗi, nhưng trong trường hợp này tôi nghĩ rằng đó là khó xảy ra.

+0

Gọn gàng, có. Nhưng nó vẫn không trả lời được câu hỏi. :/ – Nayan

+1

Vâng, đúng vậy. Bạn nên gọi vứt bỏ trên cả hai, do đó việc sử dụng tuyên bố trên cả hai. –

0

Để trả lời câu hỏi của bạn, có, bạn có thể làm điều đó.

Vì đối tượng SqlCommand bị giới hạn bởi niềng răng bên ngoài, nó sẽ được GC thu thập khi thực hiện hết khối.

câu trả lời khác cũng không sao, nhưng họ không chính xác đã trả lời câu hỏi của bạn :)

+3

Bộ sưu tập rác không giống như gọi .Dispose(). Hơn nữa, GC là không xác định, bạn không biết chính xác khi nào đối tượng sẽ được thu thập. –

+0

Ok, đã hiểu. Cảm ơn! – Nayan

+0

Dựa vào GC để thu thập SqlCommand dụ có thể gây ra bộ nhớ rất khó chịu "rò rỉ", để lại nhiều trường hợp trong bộ nhớ. – Marek

4

Bạn có thể bỏ qua bằng cách sử dụng xung quanh SqlCommand. GC cuối cùng sẽ làm sạch nó cho bạn. Tuy nhiên, tôi khuyên bạn không nên làm điều này. Tôi sẽ giải thích tại sao.

SqlCommand gián tiếp kế thừa từ System.ComponentModel.Component và do đó kế thừa phương thức Finalizer của nó. Không gọi xử lý trên SqlCommand sẽ đảm bảo rằng lệnh được quảng bá ít nhất một thế hệ sau khi nó nằm ngoài phạm vi (trình thu gom rác .NET là generational gc). Ví dụ: khi lệnh nằm trong gen 1, nó sẽ chuyển sang gen 2. Các đối tượng finalizable được lưu giữ lâu hơn trong bộ nhớ để đảm bảo trình finalizer có thể chạy an toàn. Nhưng không chỉ bản thân lệnh được giữ trong bộ nhớ, mà tất cả các đối tượng mà nó tham chiếu đều đi kèm với nó đến thế hệ đó. Các đối tượng mà nó sẽ tham chiếu là SqlConnection, danh sách các đối tượng SqlParameter, chuỗi có thể lớn hơn CommandText và nhiều đối tượng nội bộ khác mà nó tham chiếu.Bộ nhớ đó chỉ có thể được loại bỏ khi thế hệ đó được thu thập, nhưng thế hệ càng ít thì nó càng bị quét.

Do đó, không gọi điện sẽ cung cấp thêm áp lực bộ nhớ và công việc bổ sung cho chuỗi kết thúc.

Khi .NET không thể cấp phát bộ nhớ mới, CLR sẽ bắt buộc thu gom rác của tất cả các thế hệ. Sau đó, thời gian chạy thường sẽ có đủ không gian để cấp phát các đối tượng mới. Tuy nhiên, khi điều này bắt buộc thu thập xảy ra khi có rất nhiều đối tượng trong bộ nhớ vẫn phải được nâng cấp lên một gen tiếp theo (vì chúng có thể hoàn chỉnh, hoặc được tham chiếu bởi một đối tượng finalizable) có thể CLR không thể tự do đủ bộ nhớ. An OutOfMemoryException sẽ là kết quả của việc này.

Tôi phải thừa nhận rằng tôi chưa bao giờ thấy điều này xảy ra, bởi vì các nhà phát triển không chỉ xử lý các đối tượng SqlCommand của họ. Tuy nhiên, tôi đã thấy OOMs trong hệ thống sản xuất rất nhiều, gây ra bởi không xử lý các đối tượng đúng cách.

Tôi hy vọng điều này sẽ cung cấp một chút thông tin cơ bản về cách thức hoạt động của GC và rủi ro không được xử lý đúng (đối tượng cuối cùng) đúng cách. Tôi luôn bỏ tất cả các đồ vật dùng một lần. Trong khi một cái nhìn trong Reflector có thể chứng minh rằng điều này không nhất thiết đối với một kiểu nào đó, loại lập trình này dẫn đến mã ít bảo trì hơn và làm cho mã phụ thuộc vào hành vi bên trong của một kiểu (và hành vi này có thể thay đổi trong tương lai).

+0

Cảm ơn bạn đã giải thích, Steven! – Nayan

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