Как узнать, удалена ли ссылка на объект IDisposable?

86

Есть ли метод или какой-то другой легкий способ проверить, есть ли ссылка на удаленный объект?

PS - Это просто любопытство (спи спокойно, а не в продакшене). Да, я знаю, что могу поймать это ObjectDisposedExceptionпри попытке доступа к члену объекта.

Нил С. Обремски
источник
11
Не знаю. Кажется любопытным, что нет bool IsDisposed { get; }декларации System.IDisposable.
nicodemus13,
3
@ nicodemus13: Disposeметод предписывает объекту освободить все ресурсы, которые он получил, но еще не освободил. Если объект никогда не содержит ресурсов, его Disposeметод обычно ничего не делает; если тип заявляет, void IDisposable.Dispose() {};что в противном случае он может игнорировать IDisposableбез дополнительных затрат на каждый экземпляр. IsDisposedИмущество , которое , как ожидается, станет истинным после любого Disposeвызова потребует добавлений в противном случае, ненужный логический флаг для каждого экземпляра многих видов , которые могли бы игнорировать Dispose.
supercat 07
1
Но где бы вы ни вызывали метод объекта, который реализует IDisposable, как вы можете сначала проверить, был ли он удален? Вместо того, чтобы предполагать, что это не так, и ловить исключение? Или каким-то образом вы должны управлять сроком службы, чтобы всегда знать, утилизировано оно или нет?
nicodemus13
3
@ nicodemus13: обычно не следует использовать объект, не зная, что он не был и не будет удален, за исключением случаев, когда вы готовы рассматривать удаление объекта внешним кодом как сигнал для отмены любых ожидающих действий с ним . IsDisposedФлаг может помочь предотвратить код от траты времени на операции, не может добиться успеха, но один все еще нужно обрабатывать исключения в случае , если объект получает расположенный между IsDisposedпроверкой и попыткой использовать его.
supercat
WeakReferenceкажется здесь актуальным. Это не совсем детектор IDipose'd, но он говорит вам, если он GC'd
Малахи

Ответы:

48

Нет - реализация шаблона IDisposable по умолчанию не поддерживает его

Дандикас
источник
41

System.Windows.Forms.Controlимеет IsDisposedсвойство, для которого устанавливается значение true после Dispose()вызова . В ваших собственных объектах IDisposable вы можете легко создать подобное свойство.

Райан Ланди
источник
ОП искал, есть ли уже подобное свойство у объектов, которые он не создает. Это было бы хорошей идеей для объектов, которые мы создаем, но большинство одноразовых классов в .NET не следуют этому соглашению. Ответ Дандыкаса правильный.
krillgar
2
@krillgar, в вопросе ОП нет ничего, что подтверждает ваше утверждение.
Райан Ланди
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 как свойство и проверьте это.

Jop
источник
2

DisposeМетод требуется выполнить все , что очистка будет необходимо , прежде чем объект прекращается; если очистка не требуется, ничего делать не требуется. Требование от объекта отслеживать, был ли он удален, даже если в Disposeпротивном случае метод ничего бы не сделал, потребовало бы, чтобы многие IDisposableобъекты добавляли флаг для очень ограниченной выгоды.

Было бы полезно, если бы в него были IDisposableвключены два свойства: одно указывало, нужно ли удалять объект , а другое указывало, что объект не стал бесполезным в результате удаления. Для объектов, в которых удаление действительно что-то делает, оба значения будут изначально истинными, а затем станут ложными Dispose. Для объектов, удаление которых не требует какой-либо очистки, первый метод всегда может возвращать false, а второй всегда true, без необходимости хранить флаг где-либо. Однако я не думаю, что есть какой-либо способ добавить их в .NET сейчас.

суперкар
источник
ИМХО, два флага - это перебор. Я думаю, что лучше придерживаться обычной парадигмы, когда у объекта есть единственный флаг после того, как Dispose был вызван для объекта. В противном случае вы добавляете сложность, просто чтобы знать, что определенные объекты «все еще полезны», даже если для них был вызван Dispose. Не стоит идти по этой дороге.
ToolmakerSteve
@ToolmakerSteve: Обычно флагов может быть ноль или один. Для объектов, требующих удаления, свойства "требуется удаление" и "полезно" дадут значение "истина / истина" до удаления и "ложь / ложь" после него, но для объектов, для которых удаление не выполняется, оба безоговорочно вернуть «ложь / истина». Сказать, что объект все еще требует утилизации, когда этого никогда не происходит, или что объект бесполезен, когда он всегда есть, было бы довольно неприятно. Я полагаю, что другой подход заключался бы в использовании перечислимого типа, чтобы указать, нужно ли утилизировать тип, был ли удален или просто не имеет значения.
supercat 01
@ToolmakerSteve: я думаю, что основная причина IDisposableотсутствия Disposedсвойства заключается в том, что было бы странно иметь объекты, вызов Disposeкоторых не установил бы такое свойство true, но требовал, чтобы объекты отслеживали, Disposeбыл ли вызван в случаях, когда в противном случае у них не было бы причин для беспокойства, это привело бы к значительным затратам и незначительной выгоде.
supercat 01
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интерфейса.
ToolmakerSteve
-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 #, который не ничего особо экзотического).
supercat 07
3
Зачем вам делать все это вместо использования Usingоператора? Это определенно существовало еще в 2013 году, когда был написан этот ответ.
Коди Грей
На самом деле "GoodExit:" что это за 1983 год для GOTO ?? Пожалуйста, прекратите использовать это.
Моисей
Это не отвечает на вопрос. В частности, после того, inputPdfкак было установлено значение (кроме Nothing), ваш ответ не показывает способа узнать, было ли inputPdfудалено. Вы можете частично решить эту проблему, установив inputPdf = Nothingпосле утилизации. Однако это не поможет другим переменным, которые были указаны на тот же объект, что и inputPdf. То есть , если вы делаете: inputPdf = New PdfReader, Dim pdf2 As PdfReader = inputPdf, inputPdf.Dispose, inputPdf = Nothing, не было еще никакого способа узнать , что pdf2расположена (это тот же объект inputPdf).
ToolmakerSteve