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

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

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

Не знаю. Кажется любопытным, что на bool IsDisposed { get; } нет декларации System.IDisposable.

nicodemus13 09.08.2012 16:30

@ nicodemus13: метод Dispose предписывает объекту освободить все ресурсы, которые он получил, но еще не освободил. Если объект никогда не удерживает ресурсы, его метод Dispose обычно не должен ничего делать; если тип объявляет void IDisposable.Dispose() {};, он может игнорировать IDisposable без дополнительных затрат на каждый экземпляр. Свойство IsDisposed, которое должно было стать истинным после любого вызова Dispose, потребовало бы добавления ненужного в противном случае логического флага к каждому экземпляру многих типов, которые в противном случае могли бы игнорировать Dispose.

supercat 08.12.2013 02:54

Но где бы вы ни вызывали метод объекта, реализующего IDisposable, как вы можете сначала проверить, был ли он удален? Вместо того, чтобы предполагать, что это не так, и ловить исключение? Или вы каким-то образом должны управлять сроком службы, чтобы всегда знать, утилизировано оно или нет?

nicodemus13 16.12.2013 18:30

@ nicodemus13: обычно не следует использовать объект, не зная, что он не был и не будет удален, за исключением случаев, когда вы готовы рассматривать удаление объекта внешним кодом как сигнал для отмены любых ожидающих действий с ним . Флаг IsDisposed может помочь предотвратить трату времени кода на операции, которые не могут быть успешными, но все равно придется обрабатывать исключения в случае удаления объекта между проверкой IsDisposed и попыткой его использования.

supercat 13.02.2015 23:42

WeakReference кажется здесь уместным. Это не совсем детектор IDipose'd, но он говорит вам, был ли он GC.

Malachi 14.09.2018 06:24

Хотя это не помогает всем IDisposable и требует заблаговременного планирования, если у вас есть System.ComponentModel.IComponent, есть событие Disposed, к которому вы можете присоединиться, как упомянуто в Ответ Моисея

ToolmakerSteve 01.11.2018 22:17
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
89
6
44 886
7

Ответы 7

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

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

ОП искал, есть ли уже подобное свойство у объектов, которые он не создает. Это было бы хорошей идеей для объектов, которые мы создаем, но большинство одноразовых классов в .NET не следуют этому соглашению. Ответ Дандикаса правильный.

krillgar 29.05.2014 19:07

@krillgar, в вопросе ОП нет ничего, что подтверждает ваше утверждение.

Ryan Lundy 18.05.2016 15:58

Если это не ваш класс и он не предоставляет свойство IsDisposed (или что-то подобное - это просто соглашение), то у вас нет возможности узнать.

Но если это ваш класс и вы следуете каноническая реализация IDisposable, тогда просто выставьте поле _disposed или _isDisposed как свойство и проверьте это.

Нет ничего, что позволяло бы это сделать. Вам нужно будет предоставить логическое свойство 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 01.11.2018 22:23

Я не думаю, что вы можете унаследовать интерфейс из-за того, как работает C#. Размещение интерфейса после двоеточия наследует его. Я ожидаю, что он будет реализовывать интерфейс с другой стороны.

Moses 11.05.2019 07:06

Мне нравится объявлять объекты без их инициализации, но устанавливать для них значения по умолчанию 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

@ LarsHöppner: Суть вопроса не зависит от языка, и хорошие разработчики C#, вероятно, должны знать хотя бы достаточно VB.NET, чтобы прочитать приведенный выше код (и разработчики VB.NET также должны выучить достаточно C#, чтобы читать код C#, который не делать что-нибудь особо экзотическое).

supercat 08.12.2013 02:55

Зачем вам делать все это вместо использования оператора Using? Это определенно существовало еще в 2013 году, когда был написан этот ответ.

Cody Gray 05.01.2017 18:26

На самом деле "GoodExit:" что это за 1983 год для GOTO ?? Пожалуйста, прекратите использовать это.

Moses 14.09.2018 04:01

Это не отвечает на вопрос. В частности, после того, как inputPdf был установлен на значение (кроме Nothing), ваш ответ не показывает способа узнать, был ли inputPdf удален. Вы можете решить эту проблему с помощью частично, установив inputPdf = Nothing после утилизации. Однако это не поможет никаким переменным Другие, которые были указаны на тот же объект, что и inputPdf. То есть, если вы сделаете: inputPdf = New PdfReader, Dim pdf2 As PdfReader = inputPdf, inputPdf.Dispose, inputPdf = Nothing, все равно не будет возможности узнать, что pdf2 удален (это тот же объект, что и inputPdf).

ToolmakerSteve 01.11.2018 22:12

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

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

ИМХО, два флага - это перебор. Я думаю, что лучше придерживаться обычной парадигмы, когда у объекта есть единственный флаг после того, как Dispose был вызван для объекта. В противном случае вы добавляете сложность, просто чтобы знать, что определенные объекты «все еще полезны», даже если для них был вызван метод Dispose. Не стоит идти по этой дороге.

ToolmakerSteve 01.11.2018 21:53

@ToolmakerSteve: Обычно флагов может быть ноль или один. Для объектов, требующих удаления, свойства "требуется удаление" и "полезно" дадут значение "истина / истина" перед удалением и "ложь / ложь" после него, но для объектов, для которых удаление не требуется, оба безоговорочно вернуть «ложь / истина». Сказать, что объект все еще требует утилизации, когда этого никогда не происходит, или что объект бесполезен, когда он всегда есть, было бы довольно неприятно. Я полагаю, что другой подход заключался бы в использовании перечислимого типа, чтобы указать, нужно ли утилизировать тип, был ли удален или просто не имеет значения.

supercat 01.11.2018 21:58

@ToolmakerSteve: Я думаю, что основная причина, по которой IDisposable не имеет свойства Disposed, заключается в том, что было бы странно иметь объекты, в которых вызов Dispose не установил бы такое свойство в true, но требовал, чтобы объекты отслеживали, является ли Dispose вызывали в тех случаях, когда в противном случае у них не было бы причин для беспокойства, что привело бы к значительным затратам и небольшой выгоде.

supercat 01.11.2018 22:43

Я вижу, что это старый, но я не видел ответа. Некоторые не все одноразовые объекты, такие как 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 01.11.2018 21:59

Другие вопросы по теме