Đây là những gì tôi hiểu về IDisposable và finalizers từ "CLR thông qua C#", "Effective C#" và các nguồn lực khác:Singleton với finalizer nhưng không IDisposable
- IDisposable là để dọn dẹp quản lý và các nguồn lực không được quản lý tất định.
- Các lớp chịu trách nhiệm về tài nguyên không được quản lý (ví dụ: xử lý tệp) nên triển khai IDisposable và cung cấp trình xác nhận để đảm bảo rằng chúng được làm sạch ngay cả khi mã khách hàng không gọi Dispose() trên cá thể.
- Các lớp chịu trách nhiệm đối với tài nguyên được quản lý chỉ không bao giờ triển khai trình kết thúc.
- Nếu bạn có trình kết thúc thì bạn phải triển khai IDisposable (điều này cho phép mã khách hàng thực hiện đúng và gọi Dispose(), trong khi trình hoàn tất ngăn chặn tài nguyên bị rò rỉ nếu họ quên).
Mặc dù tôi hiểu lý do và đồng ý với tất cả các điều trên, có một trường hợp tôi nghĩ rằng cần phải phá vỡ các quy tắc này: một lớp đơn lẻ chịu trách nhiệm về tài nguyên không được quản lý (chẳng hạn như cung cấp một điểm truy cập vào các tệp cụ thể).
Tôi tin rằng có một phương thức Dispose() trên một singleton vì trường hợp singleton nên sống cho ứng dụng và nếu bất kỳ mã khách hàng nào gọi Dispose() thì bạn sẽ bị nhét. Tuy nhiên, bạn muốn một finalizer để khi ứng dụng được unloaded finalizer có thể dọn sạch các tài nguyên không được quản lý.
Vì vậy, có một lớp singleton với trình hoàn thiện không thực hiện IDisposable có vẻ như tôi là một điều hợp lý để làm, nhưng kiểu thiết kế này chống lại những gì tôi hiểu là thực hành tốt nhất.
Đây có phải là cách tiếp cận hợp lý không? Nếu không, tại sao không và những lựa chọn thay thế vượt trội là gì?
Sự thật là, nếu bạn có thể hình dung được lý do cho đối tượng của bạn được thay thế trong suốt thời gian của ứng dụng, đối tượng đó có thể không hợp pháp là một singleton. Còn về lý do ... Nói tôi tóm lấy Singleton và đưa nó cho những thứ cần thiết (nguyên nhân, ya biết, tiêm phụ thuộc là một điều tốt). Sau đó, một cái gì đó ở đâu đó quyết định thay thế nó. Bây giờ có * hai đơn * (cái cũ tôi đã đi qua, và cái mới). Khả năng này rất vi phạm toàn bộ định nghĩa của Singleton - tức là, mã đó luôn thấy chính xác một ví dụ. – cHao
Vì vậy, trước hết, câu trả lời cũ của câu truyện cổ tích Batman. Thứ hai, ví dụ của bạn có vẻ không hợp lý.Nếu bạn truyền một thể hiện của Singleton cho một đối tượng và sau đó một số đối tượng khác quyết định nó muốn Singleton, thì mẫu đó cho phép điều đó - nó sẽ lấy một tham chiếu về đối tượng hiện có trong bộ nhớ. Điểm tôi đã làm là mô hình không ngăn cản bạn xử lý vật thể. Trong trường hợp đó, sử dụng ví dụ của bạn, nếu đối tượng đã được xử lý thì cá thể đơn sẽ không còn tồn tại, vì vậy khi một đối tượng khác yêu cầu Singleton, nó sẽ nhận được một cá thể mới. –
Nếu đối tượng được xử lý, thể hiện * vẫn tồn tại - bất kỳ ai đã mua nó trước khi nó được xử lý vẫn sẽ có nó. Vì vậy, nó rõ ràng không thể được GCed, nhưng nó bây giờ không sử dụng được. Hãy suy nghĩ về những hậu quả của tuyên bố đó trong một bối cảnh đa luồng. Một biểu thức như 'Singleton.getInstance(). DoStuff()' không còn là luồng an toàn nữa, ngay cả khi cả hai 'getInstance' và' doStuff' đều là *. Một luồng có thể đi vào giữa hai cuộc gọi và vứt bỏ cá thể mà chúng ta vừa mua. Và điều đó thậm chí còn giả định kịch bản của mọi người luôn luôn gọi là 'getInstance' của bạn. – cHao