2009-11-09 83 views
7

Tôi có một lớp trừu tượng mà thực hiện IDisposable, như vậy:C# Vứt bỏ trừu tượng phương pháp

public abstract class ConnectionAccessor : IDisposable 
{ 
    public abstract void Dispose(); 
} 

Trong Visual Studio 2008 Team System, tôi chạy Mã Phân tích về dự án của tôi và là một trong những lời cảnh báo rằng đã đưa ra là sau đây:

Microsoft.Design: Sửa đổi 'ConnectionAccessor.Dispose()' để nó gọi Dispose (true), sau đó gọi GC.SuppressFinalize trên đối tượng hiện tại ('this' hoặc 'Me' trong Visual Basic), và sau đó trả về.

Có phải chỉ là ngớ ngẩn, cho tôi biết sửa đổi phần thân của phương pháp trừu tượng hay tôi nên làm gì đó thêm trong bất kỳ trường hợp bắt nguồn nào là Dispose?

+0

Tại sao bạn cần thêm Dispose() vào giao diện của mình? Nếu nó được kế thừa từ IDisposable thì phương thức Dispose() đã là một phần của giao diện của bạn. –

+0

Seth, bạn phải thực hiện tất cả các thành viên giao diện. Bỏ nó ra sẽ không biên dịch. –

Trả lời

12

Bạn nên làm theo mô hình truyền thống để thực hiện Dispose. Làm cho Dispose() ảo được coi là thực hành không tốt, vì mô hình thông thường nhấn mạnh việc sử dụng lại mã trong "dọn dẹp được quản lý" (API client gọi Dispose() trực tiếp hoặc qua using) và "dọn dẹp không được quản lý" (trình kết thúc cuộc gọi GC). Để nhắc nhở, mô hình là thế này:

public class Base 
{ 
    ~Base() 
    { 
     Dispose(false); 
    } 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); // so that Dispose(false) isn't called later 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     if (disposing) 
     { 
      // Dispose all owned managed objects 
     } 

     // Release unmanaged resources 
    } 
} 

chính ở đây là không có sự trùng lặp giữa finalizer và Dispose cho dọn dẹp không được quản lý, tuy nhiên bất kỳ lớp được thừa kế có thể kéo dài cả dọn dẹp và quản lý không được quản lý.

Đối với trường hợp của bạn, những gì bạn nên làm là thế này:

protected abstract void Dispose(bool disposing) 

và để lại mọi thứ khác như vậy. Thậm chí đó là giá trị đáng ngờ, vì bạn đang thực thi các lớp dẫn xuất của mình để thực hiện Dispose ngay bây giờ - và làm thế nào để bạn biết rằng tất cả chúng đều cần nó? Nếu lớp cơ sở của bạn không có gì để vứt bỏ, nhưng hầu hết các lớp dẫn xuất có thể làm (với một vài ngoại lệ, có lẽ), thì chỉ cần cung cấp một thực hiện rỗng. Đó là những gì System.IO.Stream (bản thân nó trừu tượng), do đó, có tiền lệ.

+0

Tôi muốn mọi người được khuyên nên làm điều này: http://nitoprograms.blogspot.com/2009/08/how-to-implement-idisposable-and.html - mô hình sẽ đơn giản hơn rất nhiều nếu nó không có hỗ trợ tích hợp cho các lớp kết hợp các tài nguyên được quản lý và không được quản lý. –

+0

Nếu bất kỳ lớp nào bắt nguồn từ một lớp cụ thể sẽ cần phải triển khai IDisposable và nếu một đối tượng mong muốn được cấp kiểu cơ sở có thể kết thúc tham chiếu hữu ích cuối cùng, lớp cơ sở sẽ triển khai IDisposable, ngay cả khi đại đa số các lớp dẫn xuất sẽ không cần nó. – supercat

+0

Mẫu này có lỗi thời kể từ .net 2? Sự hiểu biết của tôi là các tài nguyên không được quản lý nên được tổ chức bởi một số hình thức xử lý an toàn/quan trọng và các lớp người dùng bình thường được thực hiện không cần một trình hoàn thiện nữa. Và 'Dispose' của họ chỉ gọi là 'Dispose' trên tay cầm. – CodesInChaos

10

Cảnh báo về cơ bản cho bạn biết để triển khai Dispose pattern trong lớp học của bạn.

Các mã kết quả sẽ giống như thế này:

public abstract class ConnectionAccessor : IDisposable 
{ 
    ~ConnectionAccessor() 
    { 
     Dispose(false); 
    } 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
    } 
} 
+3

+1 để đề cập đến mẫu và chỉ ra rằng Dispose (bool) phải là phương thức ảo/trừu tượng mà các lớp dẫn xuất có thể thực hiện hoặc ghi đè, chứ không phải Dispose(). – Yoopergeek

-1

Cảnh báo thú vị. Eric Lippert, một trong những nhà thiết kế C#, viết blog về lý do tại sao các thông báo lỗi phải là "Chẩn đoán nhưng không mô tả: mô tả vấn đề, không phải là giải pháp". Read here.

+1

Nó không phải là một thông báo lỗi trình biên dịch. Đó là kết quả của việc chạy một công cụ đặc biệt chỉ ra những vấn đề có vấn đề. Không có gì sai với nó cũng chỉ ra giải pháp. –

+0

Chắc chắn. Chỉ muốn chia sẻ liên kết. Nó đến với tôi bởi vì thông điệp không nêu vấn đề nhưng chỉ đưa ra giải pháp. Hy vọng rằng có một thông điệp trước đó làm điều đó. –

+0

Đây là loại tiếp tuyến với câu hỏi ban đầu. –

1

Mặc dù nó có vẻ hơi giống như chọn nit, lời khuyên là hợp lệ. Bạn đã cho biết rằng bạn mong đợi bất kỳ loại phụ nào của ConnectionAccessor sẽ có một cái gì đó mà chúng cần phải vứt bỏ. Vì vậy, nó có vẻ tốt hơn để đảm bảo rằng dọn dẹp thích hợp được thực hiện (về các cuộc gọi GC.SuppressFinalize) bởi lớp cơ sở hơn là dựa vào từng loại phụ để làm điều đó.

tôi sử dụng mô hình xử lý nêu tại Bruce Wagners cuốn sách Effective C# mà về cơ bản là:

public class BaseClass : IDisposable 
{ 
    private bool _disposed = false; 
    ~BaseClass() 
    { 
     Dispose(false); 
    } 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(true); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     if (_disposed) 
      return; 

     if (disposing) 
     { 
      //release managed resources 
     } 

     //release unmanaged resources 

     _disposed = true; 
    } 
} 

public void Derived : BaseClass 
{ 
    private bool _disposed = false; 

    protected override void Dispose(bool disposing) 
    { 
     if (_disposed) 
      return; 

     if (disposing) 
     { 
      //release managed resources 
     } 

     //release unmanaged resources 

     base.Dispose(disposing); 
     _disposed = true; 
    } 
3

Các chuôi duy nhất tôi muốn có với câu trả lời cung cấp cho đến nay là tất cả họ đều cho rằng bạn cần để có một finalizer, mà không nhất thiết phải là trường hợp. Có một chi phí hoạt động khá quan trọng liên quan đến việc hoàn thiện, mà tôi sẽ không muốn áp đặt lên tất cả các lớp dẫn xuất của tôi nếu nó không cần thiết.

Xem this blog post bởi Joe Duffy, giải thích khi nào bạn có thể hoặc không cần trình hoàn thiện và cách triển khai đúng mẫu Vứt bỏ trong cả hai trường hợp.
Tóm tắt bài đăng trên blog của Joe, trừ khi bạn đang làm điều gì đó khá cấp thấp đối phó với bộ nhớ không được quản lý, bạn không nên triển khai trình kết thúc. Theo nguyên tắc chung, nếu lớp của bạn chỉ chứa tham chiếu đến các loại được quản lý tự triển khai IDisposable, bạn không cần trình hoàn thiện (nhưng nên triển khai IDisposable và xử lý các tài nguyên đó). Nếu bạn đang phân bổ tài nguyên không được quản lý trực tiếp từ mã của bạn (PInvoke?) Và các tài nguyên đó phải được giải phóng, bạn cần một tài nguyên. Một lớp dẫn xuất có thể luôn luôn thêm một finalizer nếu nó thực sự cần nó, nhưng buộc tất cả các lớp dẫn xuất để có finalizer bằng cách đặt nó vào lớp cơ sở khiến tất cả các lớp dẫn xuất bị ảnh hưởng bởi hiệu năng hit của các đối tượng finalizable khi mà overhead có thể không cần thiết.

+0

Cái kẹp yêu thích của tôi. Hầu hết mọi người chỉ cần không cố gắng để hiểu điều này, và lời khuyên tốt nhất mà họ có xu hướng nhận được là chỉ cần thực hiện mô hình của Microsoft. Dưới đây là những gì tôi nghĩ là lời khuyên tốt hơn nhiều so với mẫu "chính thức": http://nitoprograms.blogspot.com/2009/08/how-to-implement-idisposable-and.html –

+0

Đó thực sự là một bài đăng trên blog tốt trên chủ đề - đơn giản và quá điểm. –

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