Điều quan trọng là phải tách riêng khỏi việc thu gom rác thải. Chúng hoàn toàn riêng biệt, với một điểm chung mà tôi sẽ đến trong một phút.
Dispose
, thu gom rác thải và quyết toán
Khi bạn viết một tuyên bố using
, đó là đường đơn giản là cú pháp cho một thử/cuối cùng khối để Dispose
được gọi là thậm chí nếu mã trong cơ thể của báo cáo kết quả using
ném một ngoại lệ. Nó không có nghĩa là đối tượng là rác được thu thập ở cuối khối.
Xử lý khoảng tài nguyên không được quản lý (tài nguyên không phải bộ nhớ). Đây có thể là xử lý giao diện người dùng, kết nối mạng, xử lý tệp, vv Đây là những tài nguyên giới hạn, do đó, bạn thường muốn phát hành chúng ngay khi có thể. Bạn nên triển khai IDisposable
bất cứ khi nào loại của bạn "sở hữu" tài nguyên không được quản lý, trực tiếp (thường là qua số IntPtr
) hoặc gián tiếp (ví dụ: qua số Stream
, số SqlConnection
v.v.).
Bộ sưu tập rác chỉ là về bộ nhớ - với một chút xoắn. Bộ thu gom rác có thể tìm thấy các đối tượng không thể tham chiếu được nữa và giải phóng chúng. Tuy nhiên, không phải lúc nào cũng tìm kiếm rác - chỉ khi nó phát hiện ra nó cần (ví dụ: nếu một "thế hệ" của heap hết bộ nhớ).
Xoay là quyết toán. Bộ thu gom rác giữ một danh sách các đối tượng không còn có thể truy cập được nữa, nhưng có một trình hoàn chỉnh (được viết là ~Foo()
trong C#, hơi gây nhầm lẫn - chúng không giống như các trình phá hủy C++). Nó chạy finalizers trên các đối tượng này, chỉ trong trường hợp họ cần phải làm thêm dọn dẹp trước khi bộ nhớ của họ được giải phóng.
Trình tổng hợp hầu như luôn được sử dụng để xóa tài nguyên trong trường hợp người dùng thuộc loại đã quên bỏ nó theo cách có trật tự. Vì vậy, nếu bạn mở một số FileStream
nhưng quên gọi Dispose
hoặc Close
, trình kết thúc sẽ cuối cùng là giải phóng xử lý tệp cơ bản cho bạn. Trong một chương trình tốt bằng văn bản, finalizers hầu như không bao giờ cháy theo ý kiến của tôi.
Thiết lập một biến để null
Một điểm nhỏ về thiết một biến để null
- điều này gần như không bao giờ yêu cầu vì lợi ích của thu gom rác thải. Đôi khi bạn có thể muốn làm điều đó nếu nó là một biến thành viên, mặc dù trong kinh nghiệm của tôi hiếm khi "phần" của một đối tượng không còn cần thiết nữa. Khi đó là biến cục bộ, JIT thường đủ thông minh (trong chế độ phát hành) để biết khi nào bạn sẽ không sử dụng lại tham chiếu. Ví dụ:
StringBuilder sb = new StringBuilder();
sb.Append("Foo");
string x = sb.ToString();
// The string and StringBuilder are already eligible
// for garbage collection here!
int y = 10;
DoSomething(y);
// These aren't helping at all!
x = null;
sb = null;
// Assume that x and sb aren't used here
Một lần, nơi nó có thể có giá trị thiết lập một biến địa phương để null
là khi bạn đang ở trong một vòng lặp, và một số chi nhánh của vòng lặp cần phải sử dụng biến nhưng bạn biết bạn đã đạt đến một điểm mà bạn không đạt được. Ví dụ:
SomeObject foo = new SomeObject();
for (int i=0; i < 100000; i++)
{
if (i == 5)
{
foo.DoSomething();
// We're not going to need it again, but the JIT
// wouldn't spot that
foo = null;
}
else
{
// Some other code
}
}
thực hiện IDisposable/finalizers
Vì vậy, nên loại riêng của bạn thực hiện finalizers? Hầu như chắc chắn là không. Nếu bạn chỉ gián tiếp giữ tài nguyên không được quản lý (ví dụ:bạn có một biến finalizer của bạn sẽ không giúp ích gì: luồng này gần như chắc chắn sẽ đủ điều kiện thu gom rác khi đối tượng của bạn, vì vậy bạn chỉ có thể dựa vào FileStream
có trình hoàn thiện (nếu cần - nó có thể ám chỉ cái gì khác, v.v.). Nếu bạn muốn giữ một tài nguyên không được quản lý "gần" trực tiếp, SafeHandle
là bạn của bạn - phải mất một chút thời gian để bắt đầu, nhưng điều đó có nghĩa là bạn sẽ almostnever need to write a finalizer again. Bạn thường chỉ cần một finalizer nếu bạn có một thực sự trực tiếp xử lý trên một nguồn lực (một IntPtr
) và bạn nên xem xét để di chuyển đến SafeHandle
càng sớm càng tốt. (Có hai liên kết ở đó - đọc cả hai, lý tưởng.)
Joe Duffy có very long set of guidelines around finalizers and IDisposable (đồng viết với rất nhiều dân gian thông minh) đáng đọc. Điều đáng lưu ý là nếu bạn niêm phong các lớp của mình, nó giúp cuộc sống trở nên dễ dàng hơn nhiều: mẫu ghi đè Dispose
để gọi phương thức ảo Dispose(bool)
ảo mới chỉ có liên quan khi lớp của bạn được thiết kế để kế thừa.
Đây là một chút của một nói dông dài, nhưng hãy hỏi để làm rõ nơi bạn muốn một số :)
Re "Một lần mà nó có thể được thiết lập giá trị một biến địa phương để null" - có lẽ cũng một số các kịch bản "nắm bắt" gai (nhiều chụp cùng biến) - nhưng nó có thể không có giá trị phức tạp các bài viết! +1 ... –
@Marc: Đúng vậy - tôi thậm chí còn không nghĩ đến các biến bị bắt. Hmm. Vâng, tôi nghĩ tôi sẽ để nó một mình;) –
Wow. Cuối cùng tôi đã hiểu được nguồn gốc của giáo phái Skeetists. Bài đăng này thật tuyệt vời! – JohnFx