2010-04-08 31 views
11

Trong hầu hết các ví dụ mà bạn tìm thấy trên web khi rõ ràng không sử dụng "sử dụng", mô hình trông giống như sau:IDisposable: là cần thiết để kiểm tra null vào cuối {}?

SqlConnection c = new SqlConnection(@"..."); 
    try { 
    c.Open(); 
    ... 
    } finally { 
    if (c != null) //<== check for null 
     c.Dispose(); 
    } 

Nếu bạn sử dụng "sử dụng" và nhìn vào mã IL được tạo ra, bạn có thể thấy rằng nó tạo ra kiểm tra cho null

L_0024: ldloc.1 
L_0025: ldnull 
L_0026: ceq 
L_0028: stloc.s CS$4$0000 
L_002a: ldloc.s CS$4$0000 
L_002c: brtrue.s L_0035 
L_002e: ldloc.1 
L_002f: callvirt instance void [mscorlib]System.IDisposable::Dispose() 
L_0034: nop 
L_0035: endfinally 

Tôi hiểu tại sao IL được dịch để kiểm tra null (không biết bạn đã làm gì trong khối sử dụng), nhưng nếu bạn đang dùng thử .. cuối cùng và bạn có toàn quyền kiểm soát cách đối tượng IDisposable được sử dụng bên trong try..finally bl ock, bạn có thực sự cần phải kiểm tra null? nếu vậy, tại sao?

+4

Có thể bảo vệ chống lại ngoại lệ tham chiếu Null trong trường hợp bạn đặt biến thành null trong khối sử dụng/thử không? – Gishu

+0

đó là những gì tôi đang suy nghĩ – BlackTigerX

+0

Câu hỏi hay .. làm cho tôi nhận ra pad doodle mã của tôi và tìm hiểu một cái gì đó mới. – Gishu

Trả lời

12

"bằng cách sử dụng" báo cáo có thể khởi tạo biến với các cuộc gọi khác hơn là nhà xây dựng. Ví dụ:

using (Foo f = GetFoo()) 
{ 
    ... 
} 

Đây f có thể dễ dàng được null - trong khi một cuộc gọi constructor có thể không bao giờ trở lại null. Đó là lý do tại sao một kiểm tra tuyên bố using cho vô hiệu. Nó không liên quan đến những gì bên trong khối, bởi vì câu lệnh using duy trì giá trị ban đầu ban đầu. Nếu bạn viết:

Stream s; 
using (s = File.OpenRead("foo.txt")) 
{ 
    s = null; 
} 

thì luồng vẫn sẽ được xử lý. (Nếu biến là tuyên bố ở phần khởi tạo của bản tuyên bố using, nó chỉ đọc anyway.)

Trong trường hợp của bạn, như bạn biết rằng c là không null trước khi bạn nhập khối try, bạn don 't cần phải kiểm tra null trong khối finally trừ khi bạn chỉ định lại giá trị của nó (mà tôi chân thành hy vọng bạn không!) trong khối.

Bây giờ với mã hiện tại của bạn có nguy cơ nhẹ rằng một ngoại lệ không đồng bộ có thể được ném giữa sự phân công để c và nhập khối try - nhưng thật khó để tránh loại này tình trạng chủng tộc hoàn toàn, vì có thể không kém là một ngoại lệ không đồng bộ sau khi hàm tạo đã hoàn thành nhưng trước khi giá trị được gán cho c. Tôi sẽ đề nghị rằng hầu hết các nhà phát triển không cần phải lo lắng về điều này loại điều - ngoại lệ không đồng bộ có xu hướng đủ "cứng" mà họ sẽ mang lại quá trình anyway.

Có lý do nào bạn không muốn chỉ sử dụng câu lệnh sử dụng không? Thành thật mà nói, tôi rất hiếm khi viết các khối finally của riêng mình vào những ngày này ...


Xem câu trả lời của Marc và khóc. Không thường có liên quan mặc dù.

+0

có, có một lý do trong trường hợp cụ thể này * không * sử dụng câu lệnh sử dụng, thông thường tôi sử dụng và không lo lắng về nó – BlackTigerX

+0

trường hợp cụ thể của tôi hoặc tạo ra một thể hiện của SqlConnection, hoặc có một thể hiện trực tiếp và sử dụng nó, cuối cùng tôi phải vứt bỏ trong trường hợp nó không phải là một "live instance" – BlackTigerX

+0

"Trong trường hợp của bạn, như bạn biết rằng c là không null trước khi bạn nhập khối try, bạn không cần phải kiểm tra null trong khối cuối cùng trừ khi bạn gán lại giá trị của nó (mà tôi chân thành hy vọng bạn không!) trong khối. " điều này trả lời câu hỏi của tôi, cảm ơn! – BlackTigerX

5

Trong ví dụ của bạn, kiểm tra null là không cần thiết, vì c không thể rỗng sau khi xây dựng rõ ràng.

Trong ví dụ sau (tương tự như những gì được tạo ra bởi tuyên bố sử dụng), một tấm séc for null sẽ tất nhiên là cần thiết:

SqlConnection c = null; 
    try { 
    c = new SqlConnection(@"..."); 
    c.Open(); 
    ... 
    } finally { 
    if (c != null) //<== check for null 
     c.Dispose(); 
    } 

Bên cạnh đó, một tấm séc for null sẽ được yêu cầu trong những điều sau đây trường hợp, như bạn không thể chắc chắn CreateConnection sẽ không trả lại null:

SqlConnection c = CreateConnection(...); 
    try { 
    c.Open(); 
    ... 
    } finally { 
    if (c != null) //<== check for null 
     c.Dispose(); 
    } 
+0

Bạn không biết rằng phần "..." của mã của anh ấy không phải là "c = null;" vì vậy việc kiểm tra sẽ là cần thiết. –

+0

@Matt - đúng, nhưng có lẽ anh ấy biết những gì ở đó. – Joe

+0

Cảm giác của tôi là bởi vì anh ấy đọc nó từ các ví dụ mã, họ có thể có "..." quá và các tác giả không dùng bất kỳ cơ hội nào. –

2

Jon đã được đề cập đến điểm chính ở đây ... nhưng chỉ là một phần ngẫu nhiên của câu đố; nhà xây dựng của bạn có thể trả lại null. Không thực sự là một trường hợp phổ biến, và chắc chắn không phải lý do thực sự mà using thực hiện điều này và bạn thực sự không nên tra tấn mã của bạn cho việc này - nhưng it can happen (tìm kiếm MyFunnyProxy).

+0

sẽ không chỉ ném một ngoại lệ nếu nó không thể tạo ra một thể hiện của SqlConnection? – BlackTigerX

+0

kiểm tra nhận xét của Jon: "trong khi lời gọi hàm tạo không bao giờ có thể trả về giá trị" – BlackTigerX

+0

@BlackTigerX: Vâng, tôi cần chỉnh sửa :) –

0

Thú vị .. Tôi đang sử dụng VS2010 và tôi thấy rằng thông qua đoạn mã tùy chỉnh của tôi (R :) - sử dụng khối đá.

  • Trong khối sử dụng, Bạn không thể gán giá trị rỗng (hoặc bất kỳ thứ gì cho vấn đề đó) cho biến khối. Kết quả là một số compiler error CS1656
  • Tiếp theo chỉ định khối var thông qua phương thức factory, trả về giá trị rỗng. Trong trường hợp này, một khối sử dụng rỗng không thông minh gọi là Vứt bỏ. (Rõ ràng, bạn sẽ nhận được một NullReferenceException nếu bạn cố gắng sử dụng khối var)
  • Tiếp theo trong một khối thử, không có quy tắc, bạn có thể gán null cho biến. Do đó một kiểm tra null trước khi Dispose là bắt buộc trong khối cuối cùng.
Các vấn đề liên quan