2009-03-10 12 views
9

Tôi hiểu rằng nó được sử dụng để deallocate tài nguyên không được quản lý, tuy nhiên, tôi bối rối như khi Dispose thực sự được gọi là. Tôi biết nó được gọi là ở cuối của một khối using, nhưng nó cũng được gọi khi đối tượng được thu thập rác?Giao diện IDisposable hoạt động như thế nào?

+0

Xin lỗi, Mark, nhưng bạn đang sủa cây sai, có thể trong khu rừng sai. Nhìn vào các tài liệu tham khảo trong các câu trả lời khác nếu bạn không tin tôi. –

+1

Wow - tại sao một người nào đó bỏ phiếu để đóng? Đây là một cuộc tranh luận tuyệt vời !! –

+0

Tôi sẽ phải lấy lại xác nhận trước đó của tôi: MS nói rằng một cặp Dispose/Finalize là Ok. Cá nhân, tôi không thích cách tiếp cận này vì những lý do được nêu bên dưới nhưng ktrauberman đã tìm thấy một tài liệu tham khảo chứng minh rằng tôi không nên quá kiên cường vào thời điểm này ... vì vậy tôi mang lại lợi nhuận. –

Trả lời

11

Nếu bạn triển khai IDisposable chính xác, bạn cũng nên bao gồm trình hoàn thiện sẽ gọi Dispose() trên đối tượng của bạn.

Nếu bạn làm điều đó, nó sẽ được GC gọi. Tuy nhiên, nó vẫn là một ý tưởng rất tốt để cố gắng để luôn luôn vứt bỏ các đối tượng này cho mình.

Vấn đề lớn nhất khi dựa vào trình hoàn thiện để gọi Vứt bỏ là nó sẽ xảy ra trong một chuỗi khác mà bạn không kiểm soát. Điều này có thể có hậu quả khó chịu trong một số trường hợp nhất định, bao gồm gây ra ngoại lệ xảy ra trong chuỗi GC, điều này không tốt, cũng như có trường được xử lý mà bạn kiểm tra. Đây cũng là một phần của lý do tại sao bao gồm GC.SuppressFinalize (điều này) trong phương thức Dispose() của bạn là quan trọng - khi một đối tượng được xử lý, bạn không muốn tái xử lý nó.

+0

Xin lỗi, Mark, nhưng bộ sưu tập rác C# không phải là cây đũa thần mà bạn đang ngụ ý. Nếu một đối tượng thực hiện IDispose, bạn nên xử lý nó một cách rõ ràng, tốt nhất là thông qua việc sử dụng. Lời khuyên của bạn là hoàn toàn đúng đối với các đối tượng khác mặc dù - nhưng tài nguyên không nhớ là nguy hiểm! –

+0

Nếu người dùng không bao giờ gọi Dispose() và họ không sử dụng khối sử dụng, mọi tài nguyên không được quản lý sẽ không bao giờ bị xóa. Ở đây nó phụ thuộc vào lý do tại sao lớp của bạn đang thực hiện IDiposable, nhưng câu hỏi đã nêu rõ "để deallocate tài nguyên không được quản lý". Finalizers tồn tại cho chính xác lý do này. –

+0

Tôi không chắc chắn rằng chúng tôi không đồng ý với Pontus; Tôi tạo các lớp IDisposable cụ thể để sử dụng trong khối Sử dụng. Nếu không thì bạn thực sự cần phải gọi nó một cách rõ ràng để giải phóng tài nguyên không được quản lý, kết nối phát hành, v.v. –

2

Vứt bỏ() được gọi ở cuối khối Sử dụng để bạn có thể đếm trên các hành động Vứt bỏ (ví dụ như đóng kết nối db) để diễn ra khi đối tượng nằm ngoài phạm vi. Đơn giản như thế.

Cập nhật: không có gì cụ thể cho không được quản lý tài nguyên trong cuộc gọi để Vứt bỏ (mặc dù đó là cách sử dụng phổ biến).

Cập nhật 2: có một chút tranh luận về chủ đề mà Reed Copsey bắt đầu hữu ích cho việc hiểu IDisposable. Tôi khuyên bạn nên this article cho những người muốn biết nhiều hơn.

Tóm lại, một lớp IDisposable cho phép bạn xử lý rõ ràng việc deallocation tài nguyên (thường không được quản lý tài nguyên hoặc kết nối cơ sở dữ liệu) thông qua phương thức Dispose(). Các cá thể lớp IDisposable nên được tạo trong một khối "Using" để đảm bảo rằng phương thức Dispose thực sự được gọi. Nếu bạn không làm điều này (hoặc gọi nó một cách rõ ràng trong một khối "cuối cùng", vv) sau đó phương pháp Vứt bỏ của bạn sẽ không được gọi và bạn sẽ mồ côi các đối tượng bạn muốn dọn dẹp. Trong tất cả trường hợp, tôi đặt các lớp Dùng một lần trong Sử dụng khối và bạn cũng nên làm như vậy.

Như một giải pháp thay thế, bạn có thể xử lý việc dọn sạch tài nguyên trong Trình hoàn tất (một lớp Trình phá hủy). Điều này sẽ được gọi tự động khi lớp học là GC'd. Những bất lợi của phương pháp này là bạn sẽ không kiểm soát rõ ràng khi các đối tượng được dọn dẹp và có một số vấn đề về luồng để tranh luận. Vì vậy, ví dụ, các vấn đề trong Destructors là rất khó để gỡ lỗi do thực tế là chúng được gọi là không đồng bộ và trong một chủ đề khác nhau. Ưu điểm duy nhất là bạn không phải nhớ gọi Destructor của bạn như bạn làm Dispose. Tất nhiên, nếu bạn luôn sử dụng Sử dụng các khối, đây không phải là vấn đề.

LƯU Ý: Tôi ktrauberman, Reed và Pontus đã thực hiện một số điểm tốt về cách bạn có thể nhận được xung quanh những điểm tôi thực hiện bên dưới. Đây là những gì tôi làm, nhưng tôi không thể tranh luận rằng đây là cách duy nhất để làm mọi thứ. Thật vậy, Microsoft thậm chí khuyên bạn nên gọi Dispose() từ Finalizer của bạn trong một số trường hợp. Tuy nhiên, tôi sẽ để lại cuộc thảo luận ở đây chỉ là một minh họa về lý do tại sao điều quan trọng là phải làm theo lời khuyên của Reed: hãy cẩn thận khi trộn Destructors và Dispose().

Nơi tôi không đồng ý với câu trả lời của Reed là trong xác nhận rằng bạn nên triển khai lớp IDisposable bằng cách gọi Dispose() trong Trình hoàn tất của bạn. Điều này chỉ làm xáo trộn hai cấu trúc khác nhau và có thể dẫn đến các vấn đề. Ví dụ, nếu bạn tạo cá thể lớp IDisposable của bạn trong một khối Sử dụng bạn gọi là Dispose() trong Destructor, nó sẽ được gọi hai lần - có khả năng gây khó chịu và khó gỡ lỗi (một lần nữa - bạn không kiểm soát thời gian của GC). (Side lưu ý: đây là thực sự thực sự của Destructors nói chung - họ có thể, trong một số trường hợp được gọi là nhiều hơn một lần!)

+0

Đánh dấu, Bài viết bạn liên kết mâu thuẫn với vị trí của bạn. Nó nói trên trang 3 "Microsoft khuyến cáo rằng bạn nên thực hiện cả Dispose và Finalize khi làm việc với các tài nguyên không được quản lý." Trên trang 4, nó cũng cho thấy một ví dụ mà nó gọi là Dispose() từ destructor. –

+0

Tôi không nghĩ rằng bạn hiểu hoàn toàn những gì GC.SupressFinalize() làm. Bạn gọi nó ở cuối phương thức Dispose() của bạn, vì vậy nếu nhà phát triển gọi Dispose() thì bộ gom rác không bao giờ chạy trình finalizer, vì vậy việc dọn dẹp chỉ xảy ra một lần. –

+0

kt - cảm ơn vì cuộc tranh luận. Tôi không trộn lẫn hai nhưng, như tôi đã nói ở trên, tôi phải thừa nhận quan điểm của bạn và làm như vậy vui vẻ. Các cuộc tranh luận về SO luôn tốt cho việc đẩy phong bì của những gì chúng ta biết. –

2

Dispose được gọi ở một vài nơi:

  1. Vào cuối của một sử dụng khối.
  2. Khi được gọi một cách rõ ràng (ví dụ: thử {} cuối cùng {}.)

Chúng tôi khuyên bạn nên tự gọi nó khi bạn đã hoàn thành với một tài nguyên, để quản lý tài nguyên tốt hơn.

EDIT: Tôi đã nhầm. Vứt bỏ KHÔNG được gọi trong khi thu gom rác thải. Xem bài viết this.

+0

Vứt bỏ() không được gọi tự động trong khi thu gom rác thải. –

+0

+1 cho nhận xét của Brian. –

+0

Chỉ cần chỉnh sửa. Tôi đã làm một số nghiên cứu sau khi đăng bài (mà tôi nên làm trước) xấu của tôi. –

2

Không nó không được gọi khi đối tượng là thu gom rác thải. Nếu bạn muốn hành vi đó bạn có thể sử dụng destructor (finalizer) và gọi Dispose() từ đó.

Như bạn nói, nó được tự động gọi và kết thúc là khối using.