2010-09-03 61 views
6

Tôi sử dụng báo cáo sử dụng cho SqlConnection. Nó là tốt cho hiệu suất bởi vì các lực lượng gọi Dispose() mà chỉ đơn giản là phát hành kết nối với hồ bơi sớm hơn.Câu lệnh sử dụng có thể được thay thế bằng dấu ngoặc nhọn không?

Tuy nhiên, tôi nhận ra rằng đối tượng được tạo trong sử dụng không thể được định nghĩa lại. Tôi không thể làm như thế này:

using (SqlConnection connection = new SqlConnection(connectionString)) 
    { 
     connection.Open(); 
     //... 
     connection = new SqlConnection(connectionString2); 
     //... 
     connection = new SqlConnection(connectionString3); 
    } 

tôi đã tự hỏi nếu tôi có thể thay thế sử dụng, và làm điều gì đó như thế này:

{ 
     SqlConnection connection = new SqlConnection(connectionString); 

     connection.Open(); 
     //... 
     connection = new SqlConnection(connectionString2); 
     //... 
     connection = new SqlConnection(connectionString3); 
} 

Các SqlConnection sẽ không được truy xuất sau } cú đúp cuối cùng. Sẽ là Dispose() được gọi là ngay lập tức khi đối tượng đi ra khỏi phạm vi?

Trả lời

13

Không, mọi thứ sẽ không tự động được dọn dẹp trong ví dụ thứ hai của bạn (trên thực tế, với mã bạn có, bạn sẽ để lại một số kết nối treo mở).

Không chỉ vậy, nhưng bạn mất tự động dọn dẹp trong trường hợp ngoại lệ được ném vào khối using. Hãy nhớ rằng một khối using phân hủy thành:

SqlConnection connection = new SqlConnection(connectionString); 
try 
{ 
    connection.Open(); 
    // Do work 
} 
finally 
{ 
    connection.Dispose(); 
} 

Nếu bạn đang thực sự sử dụng các kết nối khác nhau và mỗi kết nối được xử lý ở phần cuối của khối, tôi sẽ sử dụng một vài khối sử dụng:

using(SqlConnection connection = new SqlConnection(connectionString)) 
{ 
    connection.Open(); 
    // Do Work 
} 

// First connection is disposed 

using(SqlConnection connection = new SqlConnection(connectionString2)) 
{ 
    // Do More Work 
} 

// Second connection is disposed 

using(SqlConnection connection = new SqlConnection(connectionString3)) 
{ 
    // Do More Work 
} 

// Last connection is dipsosed 
+0

Cảm ơn. Nếu nói đến kết nối đóng cửa, tất nhiên tôi sẽ gọi connection.Close() nhưng tôi không viết nó trong đoạn mã trên. – nan

+1

Thậm chí nếu bạn gọi Close() một cách rõ ràng, ví dụ thứ hai của bạn vẫn có khả năng để lại một kết nối mở nếu một ngoại lệ xảy ra. Không chỉ sử dụng cuộc gọi Dispose khi thực hiện rời khỏi khối đó, nó cũng gọi nó khi một ngoại lệ xảy ra. – ThatBlairGuy

3

Không, việc sử dụng tạo ra một số cấu trúc làm sạch cụ thể mà bạn sẽ không nhận được chỉ bằng niềng răng.

Nếu bạn sử dụng Reflector và nhìn vào IL có một cuộc gọi để xử lý bổ sung vào cuối bằng cách sử dụng khối cho mục tiêu của mình:

L_0006: newobj instance void [System.Data]System.Data.SqlClient.SqlConnection::.ctor(string) 
L_000b: stloc.0 
L_000c: nop 
L_000d: nop 
L_000e: leave.s L_0020 
L_0010: ldloc.0 
L_0011: ldnull 
L_0012: ceq 
L_0014: stloc.1 
L_0015: ldloc.1 
L_0016: brtrue.s L_001f 
L_0018: ldloc.0 
**L_0019: callvirt instance void [mscorlib]System.IDisposable::Dispose()** 
L_001e: nop 
L_001f: endfinally 

Điều này giải thích lý do tại sao bạn không thể tạo một kết nối mới bên trong sử dụng khối và gán nó cho biến - làm như vậy sẽ để nguyên tham chiếu gốc bị treo và không bị đánh cắp.

+1

Tôi nghĩ rằng nó gọi Dispose() trên đối tượng IDisposable ... không biết chắc chắn –

+1

@Mike - Bạn chỉ có thể sử dụng 'using' trên đối tượng IDisposable vì nó gọi Dispose() – annakata

+3

Nó thực sự gọi' ((IDisposable)) blah) .Dispose() ', trong đó' blah' là biến sử dụng. Nó không biên dịch nếu loại không triển khai 'IDisposable'. – Timwi

3

Không, nó sẽ không được gọi sau khi đóng ngoặc nhọn. Bạn cần gọi nó theo cách thủ công hoặc sử dụng câu lệnh using.

Nếu bạn không thực hiện việc xử lý thì nó sẽ được thực hiện khi hoàn thành được gọi. Và ở đây bạn có 2 vấn đề

  • Thực hiện phương pháp hoàn thiện tốn kém về hiệu suất. Nếu phương thức Dispose của bạn đã thực hiện công việc dọn dẹp đối tượng, thì nó không cần thiết cho bộ thu gom rác để gọi phương thức Finalize của đối tượng (nếu nó được triển khai tốt, phương thức Dispose nên gọi phương thức SuppressFinalize cho đối tượng mà nó đang xử lý). (MSDN)
  • Bạn không kiểm soát thời điểm khi hoàn thành tự động được gọi và không thể thực hiện được (vì một sự cố chẳng hạn).
8

Các using tuyên bố là đường cú pháp mà các cuộc gọi Dispose trên các đối tượng khởi tạo trong (), vì vậy bạn có thể không chỉ đơn giản là thay thế nó là bạn có trong ví dụ của bạn.

Bạn sẽ lưu ý rằng các đối tượng duy nhất mà bạn có thể sử dụng trong câu lệnh using là những đối tượng thực hiện IDisposable, đảm bảo rằng Dispose có thể được gọi.

Như this bài viết giải thích, trình biên dịch sẽ biến đổi này:

using (MyResource myRes = new MyResource()) 
{ 
    myRes.DoSomething(); 

} 

Để này:

MyResource myRes= new MyResource(); 
try 
{ 
    myRes.DoSomething(); 
} 
finally 
{ 
    if (myRes!= null) 
     ((IDisposable)myRes).Dispose(); 
} 

Vì vậy, trừ khi bạn sao chép cấu trúc này, bạn sẽ không nhận được hành vi tương tự.

Ngoài ra - sử dụng lại biến số theo cách bạn đang làm trong ví dụ của bạn là thực hành không tốt.Một người đọc mã có thể nghĩ rằng họ đang xem xét kết nối 1 khi họ đang trong thực tế nhìn vào 2 hoặc 3. Có thể kết thúc rất khó hiểu và gây ra tất cả các loại vấn đề xuống đường.

+0

Vâng, tôi đã cố gắng để loại bỏ việc sử dụng bởi vì tôi có rất nhiều người trong số họ (tôi cũng sử dụng chúng thường xuyên với 'Suối' trong dự án của tôi). Lý do thứ hai là tôi có rất nhiều kết nối tuần tự nên tôi đã nghĩ đến việc sử dụng một vật thể. Tôi muốn tăng độ dễ đọc nhưng bạn nói đúng, nó có thể làm giảm nó. – nan

3
using(foo) 
{ 
    // stuff 
} 

... là một chút đường, có thể dịch thành:

try 
{ 
    // stuff 
} 
finally 
{ 
    foo.Dispose() 
} 

trong đó:

{ 
    // stuff 
} 

... không dịch sang bất cứ thứ gì. Nó chỉ:

{ 
    // stuff 
} 

: D

Edit:.. Xin đừng phá hủy định dạng khi bạn chỉnh sửa :(

2

Không, bạn không thể làm điều này ít nhất trong C#

Nhưng bạn có thể tạo các đối tượng dùng một lần khác nhau bên trong một sử dụng tuyên bố:

using (SqlConnection connection1 = new SqlConnection(connectionString1), 
      connection2 = new SqlConnection(connectionString2), 
      connection3 = new SqlConnection(connectionString3)) 
    { 
     connection1.Open(); 
     //... 
     connection2.Open(); 
     //... 
     connection3.Open(); 
    } 

C++/CLI bạn có thể sử dụng các lớp dùng một lần của bạn trong ngăn xếp như faision:

void MyClass::Foo() 
{ 

{ 
    SqlConnection connection(connectionString); 
    //connection still allocated on the managed heap 
    connection.Open(); 
    ... 
    //connection will be automatically disposed 
    //when the object gets out of scope 
} 


{ 
    SqlConnection connection(connectionString2); 
    connection2.Open(); 
    ... 
} 

} 
+0

+1 cho nhóm 'sử dụng' đẹp, không biết về điều đó – nan

1

Tôi nghĩ điều này quá lúc đầu ... nhưng dường như khi khối sử dụng kết thúc, .Dispose() được gọi trên đối tượng của bạn, đó là khác với việc để vật thể ra khỏi phạm vi. Bởi vì C# là một ngôn ngữ thu thập rác, có thể mất thời gian trước khi đối tượng của bạn thực sự được dọn sạch. Khối using đảm bảo rằng nó được xử lý ngay lập tức và không phụ thuộc vào bất kỳ ngoại lệ nào.

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