如何判断是否已放置IDisposable对象引用?


85

是否有一种方法或其他轻量级的方法来检查引用是否指向已处置的物体?

PS-这只是一种好奇(睡得好,不在生产代码中)。是的,我知道我可以ObjectDisposedException尝试访问该对象的成员。


11
不知道。似乎没有bool IsDisposed { get; }声明System.IDisposable
nicodemus13 2012年

3
@ nicodemus13:该Dispose方法指示对象释放其已获取但尚未释放的所有资源。如果对象从不拥有资源,则其Dispose方法通常无需执行任何操作。如果类型声明,void IDisposable.Dispose() {};则可以忽略IDisposable每个实例,而不会产生每个实例的开销。IsDisposed预期在任何Dispose调用后变为true的属性将有必要向许多本可以忽略的许多类型的每个实例添加一个不必要的布尔标志Dispose
2013年

1
但是,无论您在实现的对象上调用方法的何处IDisposable,如何检查它是否已被先处理掉?而不是假设它不是并捕获异常?还是以某种方式管理生命周期,以便您始终应该知道它是否已废弃?
nicodemus13 2013年

3
@ nicodemus13:通常不应该使用一个对象而不知道它不会被处置,除非在准备将外部代码处置该对象视为中止任何未决操作的信号的情况下。一个IsDisposed标志可以帮助防止代码上不可能成功操作浪费时间,但人们仍需要一个对象被置于之间的情况来处理异常IsDisposed检查,并使用它的尝试。
超级猫

WeakReference在这里似乎相关。这是不完全的IDipose'd探测器,但它并告诉你,如果它是GC'd
玛拉基

Answers:



41

System.Windows.Forms.Control具有的IsDisposed属性,该属性Dispose()调用后设置为true。在您自己的IDisposable对象中,您可以轻松创建类似的属性。


OP正在查看他没有创建的对象是否已经存在类似的属性。对于我们创建的对象来说,这将是一个好主意,但是.NET中的大多数一次性类都不遵循此约定。丹迪卡斯的回答是正确的。
krillgar

2
@ krillgar,OP的问题中没有任何东西可以支持您的主张。
瑞安·伦迪

18

没有内置的功能可以做到这一点。您需要公开一个IsDisposed布尔属性,该属性反映内部已处置标志。

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);
    }
}

顺便说一句,如果人们开始使用这种模式,它将有助于定义一个IDisposablePlus继承自IDisposable并包括的新接口(或其他任何接口)bool IsDisposed { get; }。这样就很容易知道您的IDisposable对象支持哪些对象IsDisposed
ToolmakerSteve

由于C#的工作原理,我认为您不能继承接口。在冒号继承后放置接口。我希望它将在另一个上实现该接口。
摩西

9

如果不是您的类,并且没有提供IsDisposed属性(或类似的东西-名称仅是约定),那么您将无所适从。

但是,如果这是您的类,并且您正在遵循规范的IDisposable实现,则只需将_disposed或_isDisposed字段公开为属性并进行检查即可。


2

Dispose放弃对象之前,需要使用该方法执行所需的任何清理工作;如果不需要清理,则无需执行任何操作。即使该Dispose方法原本不采取任何措施,要求一个对象来跟踪其是否已被处置,也将需要许多IDisposable对象添加一个标志,以获取非常有限的收益。

如果IDisposable包括两个属性,可能会有所帮助:一个属性指示是否需要处置某个对象,另一个属性指示该对象并未因处置而变得无用。对于实际上可以处理的对象,两个值最初都是true,之后是false Dispose。对于不需要进行任何清理的对象,第一种方法可以始终返回false,第二种方法始终可以为true,而不必在任何地方存储标志。我不认为现在可以将它们添加到.NET中。


恕我直言,两个标志是过大的。我认为最好坚持通常的范例,即一旦在对象上调用了Dispose,则该范例只有一个标志。否则,您就增加了复杂性,只是知道某些对象“仍然有用”,即使已对它们调用了Dispose。走那条路不值得。
ToolmakerSteve

@ToolmakerSteve:通常会有零个或一个标志。对于需要处理的对象,“需要处理”和“有用”属性在处理之前会产生“ true / true”,在处理之后会产生“ false / false”,但是对于处理为无操作的对象,两者都是无条件的返回“ false / true”。说一个对象永远不需要处理,或者说一个对象永远没有用就说,这是很棘手的。我想另一种方法是使用枚举类型来指示类型是否需要处置,已经处置或根本不在乎。
超级猫

@ToolmakerSteve:我认为IDisposable没有Disposed属性的主要原因是,在Dispose没有将调用设置为此类属性的对象的情况下,将其视为奇怪的事情true,但是要求对象跟踪Dispose在以下情况下是否调用了该对象否则他们将没有理由去照料,这会增加可观的成本,却几乎没有收益。
超级猫

1

我看到这很旧,但是没有看到答案。并非所有一次性对象(例如DataSet)都有可以附加的已处置事件。

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;
    }
}

很高兴知道。具体来说,Disposed事件是System.ComponentModel.IComponent接口的成员。
制造商史蒂夫(Steve)'18年

-1

我想做的是声明对象而不初始化它们,但是将它们的默认值设置为Nothing。然后,在循环结束时,我写:

If anObject IsNot Nothing Then anObject.Dispose()

这是一个完整的示例:

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

这对于将主要对象放在例程的顶部,在例程中使用它们Try,然后将它们放在一个Finally块中也非常有用:

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:问题的本质是与语言无关的,好的C#开发人员应该至少知道足够的VB.NET才能阅读上述代码(而VB.NET开发人员也应该学习足够的C#来阅读不懂的C#代码做任何特别有异国情调的事情。
2013年

3
为什么不使用Using语句就执行所有这些操作?早在2013年撰写此答案时,确实存在这种情况。
科迪·格雷

真的是“ GoodExit:” 1983年的GOTO是什么?请停止使用。
摩西

这不能回答问题。具体来说,一旦inputPdf将其设置为值(Nothing除外),您的答案将无法显示是否inputPdf已处置的方法。您可以通过处理后进行设置来部分解决此问题inputPdf = Nothing。但是,这对任何其他指向与相同对象的变量都没有帮助inputPdf。也就是说,如果你这样做: inputPdf = New PdfReaderDim pdf2 As PdfReader = inputPdfinputPdf.DisposeinputPdf = Nothing,仍然会有没有办法知道,pdf2被布置(它是相同的对象inputPdf)。
制造商史蒂夫(Steve)'18年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.