Làm thế nào để biết liệu một tham chiếu đối tượng IDisposable có được xử lý hay không?


85

Có một phương pháp, hoặc một số cách nhẹ nhàng khác, để kiểm tra xem một tham chiếu có phải là một đối tượng đã xử lý không?

Tái bút - Đây chỉ là sự tò mò (ngủ ngon, không có trong mã sản xuất). Có, tôi biết tôi có thể bắt được ObjectDisposedExceptionkhi cố gắng truy cập vào một thành viên của đối tượng.


11
Không biết. Có vẻ như tò mò rằng không có bool IsDisposed { get; }tuyên bố trên System.IDisposable.
nicodemus 13

3
@ nicodemus13: DisposePhương thức này hướng một đối tượng giải phóng bất kỳ và tất cả các tài nguyên mà nó đã có được nhưng chưa được giải phóng. Nếu một đối tượng không bao giờ giữ tài nguyên, thì Disposephương thức của nó thường sẽ không phải làm gì cả; nếu kiểu khai báo void IDisposable.Dispose() {};thì nó có thể bỏ qua IDisposablemà không có chi phí cho mỗi trường hợp. Một thuộc IsDisposedtính được mong đợi trở thành true sau bất kỳ Disposelệnh gọi nào sẽ đòi hỏi phải thêm một cờ Boolean không cần thiết vào mọi trường hợp của nhiều kiểu mà nếu không thì có thể bỏ qua Dispose.
supercat

1
Tuy nhiên, bất cứ nơi nào bạn gọi một phương thức trên một đối tượng triển khai IDisposable, làm thế nào bạn có thể kiểm tra xem nó có bị loại bỏ trước hay không? Thay vì giả sử nó không phải là và bắt một ngoại lệ? Hoặc bằng cách nào đó, bạn muốn quản lý cuộc đời để bạn luôn biết liệu nó có bị loại bỏ hay không?
nicodemus 13

3
@ nicodemus13: Người ta thường không nên sử dụng một đối tượng mà không biết rằng nó chưa được và sẽ không bị xử lý trừ trường hợp người ta chuẩn bị coi việc vứt bỏ đối tượng bằng mã bên ngoài như một tín hiệu để hủy bỏ mọi hành động đang chờ xử lý với nó . Một IsDisposedcờ có thể giúp ngăn mã mất thời gian cho các hoạt động không thể thành công, nhưng người ta vẫn cần phải xử lý các ngoại lệ trong trường hợp một đối tượng bị xử lý giữa lần IsDisposedkiểm tra và cố gắng sử dụng nó.
supercat

WeakReferencecó vẻ phù hợp ở đây. Nó không phải chính xác một máy dò IDipose'd, nhưng nó cho bạn biết nếu nó GC'd
Malachi

Câu trả lời:


47

Không - triển khai mặc định của mẫu IDisposable không hỗ trợ nó


41

System.Windows.Forms.Controlcó một thuộc IsDisposedtính được đặt thành true sau khi Dispose()được gọi . Trong các đối tượng IDisposable của riêng bạn, bạn có thể dễ dàng tạo một thuộc tính tương tự.


OP đang xem xét liệu có thuộc tính tương tự trên các đối tượng mà anh ta không tạo hay không. Đây sẽ là một ý tưởng hay cho các đối tượng mà chúng ta tạo, nhưng hầu hết các lớp dùng một lần trong .NET không tuân theo quy ước này. Câu trả lời của Dandikas là đúng.
krillgar

2
@krillgar, không có gì trong câu hỏi của OP hỗ trợ khẳng định của bạn.
Ryan Lundy

18

Không có gì được xây dựng trong đó sẽ cho phép điều này. Bạn sẽ cần hiển thị thuộc tính boolean IsDisposed phản ánh một cờ xử lý nội bộ.

public class SimpleCleanup : IDisposable
{
    private bool disposed = false;

    public bool IsDisposed
    {
       get
       {
          return disposed;
       }
    }

    public SimpleCleanup()
    {
        this.handle = /*...*/;
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
               // free only managed resources here
            }

            // free unmanaged resources here
            disposed = true;
        }
    }

    public void Dispose()
    {
        Dispose(true);
    }
}

BTW, nếu một người bắt đầu sử dụng mẫu này, nó sẽ giúp xác định một giao diện mới ( IDisposablePlushoặc bất cứ thứ gì) kế thừa IDisposablevà bao gồm bool IsDisposed { get; }. Điều này giúp bạn dễ dàng biết được IDisposableđối tượng nào của bạn hỗ trợ IsDisposed.
ToolmakerSteve

Tôi không nghĩ rằng bạn có thể kế thừa một giao diện vì cách hoạt động của C #. Đặt một giao diện sau dấu hai chấm kế thừa nó. Tôi hy vọng nó sẽ triển khai giao diện trên mặt khác.
Moses,

9

Nếu nó không phải là lớp của bạn và nó không cung cấp thuộc tính IsDisposed (hoặc thứ gì đó tương tự - tên chỉ là quy ước), thì bạn không có cách nào để biết.

Nhưng nếu đó là lớp của bạn và bạn đang theo cách triển khai IDisposable chuẩn , thì chỉ cần hiển thị trường _disposed hoặc _isDisposed làm thuộc tính và kiểm tra điều đó.


2

Các Disposephương pháp được yêu cầu phải thực hiện bất cứ điều gì làm sạch sẽ được yêu cầu trước khi một đối tượng được bỏ rơi; nếu không cần dọn dẹp thì không bắt buộc phải làm gì cả. Việc yêu cầu một đối tượng theo dõi xem nó đã được xử lý hay chưa, ngay cả khi Disposephương pháp này sẽ không làm gì khác, sẽ yêu cầu nhiều IDisposableđối tượng thêm một cờ vì lợi ích rất hạn chế.

Nó có thể hữu ích nếu IDisposablebao gồm hai thuộc tính - một cho biết liệu một vật có cần vứt bỏ hay không và một trong số đó chỉ ra rằng vật đó không trở nên vô dụng khi vứt bỏ. Đối với các đối tượng mà việc xử lý thực sự làm một điều gì đó, cả hai giá trị ban đầu sẽ là true và sau đó sẽ trở thành sai Dispose. Đối với các đối tượng mà việc xử lý không cần thực hiện bất kỳ thao tác dọn dẹp nào, phương thức đầu tiên luôn có thể trả về false và phương thức thứ hai luôn đúng mà không cần phải lưu trữ cờ ở bất kỳ đâu. Tuy nhiên, tôi không nghĩ rằng có bất kỳ cách nào có thể được thêm vào .NET ngay bây giờ.


IMHO, hai lá cờ là quá mức cần thiết. Tôi nghĩ tốt hơn là nên gắn bó với mô hình thông thường, nơi người ta có một cờ duy nhất sau khi Dispose được gọi trên một đối tượng. Nếu không, bạn thêm phức tạp, chỉ để biết rằng các đối tượng nhất định "vẫn hữu ích" mặc dù Dispose đã được gọi trên chúng. Nó không đáng để đi xuống con đường đó.
ToolmakerSteve

@ToolmakerSteve: Nói chung sẽ không có hoặc một cờ. Đối với các đối tượng yêu cầu xử lý, các thuộc tính "cần xử lý" và "hữu ích" sẽ mang lại "đúng / đúng" trước khi xử lý và "sai / sai" sau đó, nhưng đối với các đối tượng mà việc xử lý sẽ là không, cả hai sẽ vô điều kiện trả về "false / true". Nói rằng một đối tượng vẫn cần vứt bỏ khi nó không bao giờ xảy ra, hoặc một đối tượng không hữu ích khi nó luôn luôn như vậy, thì đúng hơn là khá tệ. Tôi cho rằng một cách tiếp cận khác sẽ là sử dụng kiểu liệt kê để cho biết liệu một kiểu cần xử lý, đã được xử lý hay đơn giản là không quan tâm.
supercat

@ToolmakerSteve: Tôi nghĩ lý do lớn nhất IDisposablekhông có thuộc Disposedtính là nó sẽ bị coi là kỳ quặc nếu có các đối tượng mà việc gọi Disposesẽ không đặt thuộc tính như vậy true, nhưng yêu cầu các đối tượng đó phải theo dõi xem có Disposeđược gọi trong trường hợp nếu không họ sẽ không có lý do gì để quan tâm sẽ thêm chi phí đáng kể và ít lợi ích.
supercat

1

Tôi thấy điều này là cũ, nhưng tôi không thấy một câu trả lời. Một số không phải tất cả các đối tượng dùng một lần như DataSet đều có sự kiện xử lý mà bạn có thể đính kèm.

class DisposeSample : IDisposable
{
    DataSet myDataSet = new DataSet();
    private bool _isDisposed;

    public DisposeSample()
    {
        // attach dispose event for myDataSet
        myDataSet.Disposed += MyDataSet_Disposed;
    }

    private void MyDataSet_Disposed(object sender, EventArgs e)
    {
        //Event triggers when myDataSet is disposed
        _isDisposed = true; // set private bool variable as true 
    }


    public void Dispose()
    {
        if (!_isDisposed) // only dispose if has not been disposed;
            myDataSet?.Dispose(); // only dispose if myDataSet is not null;
    }
}

Tốt để biết. Đặc biệt,Disposed sự kiện là một thành viên của System.ComponentModel.IComponentgiao diện.
ToolmakerSteve

-1

Những gì tôi muốn làm là khai báo các đối tượng mà không khởi tạo chúng, nhưng đặt giá trị mặc định của chúng thành Nothing. Sau đó, ở cuối vòng lặp, tôi viết:

If anObject IsNot Nothing Then anObject.Dispose()

Đây là một mẫu hoàn chỉnh:

Public Sub Example()
    Dim inputPdf As PdfReader = Nothing, inputDoc As Document = Nothing, outputWriter As PdfWriter = Nothing

    'code goes here that may or may not end up using all three objects, 
    ' such as when I see that there aren't enough pages in the pdf once I open  
    ' the pdfreader and then abort by jumping to my cleanup routine using a goto ..

GoodExit:
    If inputPdf IsNot Nothing Then inputPdf.Dispose()
    If inputDoc IsNot Nothing Then inputDoc.Dispose()
    If outputWriter IsNot Nothing Then outputWriter.Dispose()
End Sub

Điều này cũng hoạt động tuyệt vời để đặt các đối tượng chính của bạn lên đầu một thói quen, sử dụng chúng bên trong Try quy trình và sau đó sắp xếp chúng trong một Finallykhối:

Private Sub Test()
    Dim aForm As System.Windows.Forms.Form = Nothing
    Try
        Dim sName As String = aForm.Name  'null ref should occur
    Catch ex As Exception
        'got null exception, no doubt
    Finally
        'proper disposal occurs, error or no error, initialized or not..
        If aForm IsNot Nothing Then aForm.Dispose()
    End Try
End Sub

6
@ LarsHöppner: Bản chất của câu hỏi là ngôn ngữ bất khả tri và các nhà phát triển C # giỏi có lẽ nên biết ít nhất đủ VB.NET để đọc mã trên (và các nhà phát triển VB.NET cũng nên học đủ C # để đọc mã C # mà không làm bất cứ điều gì đặc biệt kỳ lạ).
supercat

3
Tại sao bạn làm tất cả những điều này thay vì sử dụng một Usingcâu lệnh? Điều đó chắc chắn đã tồn tại vào năm 2013 khi câu trả lời này được viết ra.
Cody Grey

Thực sự "GoodExit:" năm 1983 này là gì cho một GOTO ?? Hãy ngừng sử dụng nó.
Moses

Điều này không trả lời câu hỏi. Cụ thể, một khi inputPdfđã được đặt thành một giá trị (khác với Không có gì), câu trả lời của bạn không có cách nào để biết liệu inputPdfđã bị loại bỏ hay chưa. Bạn có thể giải quyết phần nào điều này bằng cách thiết lập inputPdf = Nothingsau khi xử lý. Tuy nhiên, điều này sẽ không giúp bất kỳ biến nào khác đã được trỏ đến cùng một đối tượng như inputPdf. Đó là nếu bạn làm: inputPdf = New PdfReader, Dim pdf2 As PdfReader = inputPdf, inputPdf.Dispose, inputPdf = Nothing, sẽ vẫn không có cách nào để biết rằng pdf2được xử lý (đó là cùng một đối tượng như inputPdf).
ToolmakerSteve
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.