Як розпізнати, чи не використовується посилання на об'єкт, що одноразово використовується?


85

Чи існує метод, або якийсь інший легкий спосіб, щоб перевірити, чи посилання на віддалений об'єкт?

PS - Це просто курйоз (спати добре, не в виробничому коді). Так, я знаю, що можу зрозуміти це ObjectDisposedExceptionпри спробі отримати доступ до члена об’єкта.


11
Невідомо. Цікаво, що bool IsDisposed { get; }декларації про це немає System.IDisposable.
nicodemus13

3
@ nicodemus13: DisposeМетод спрямовує об’єкт на звільнення будь-яких ресурсів, які він придбав, але ще не випустив. Якщо об'єкт ніколи не має ресурсів, його Disposeметоду, як правило, нічого не потрібно робити; якщо тип оголошує, void IDisposable.Dispose() {};він може інакше ігнорувати IDisposableбез накладних витрат для кожного екземпляра. IsDisposedМайно , яке , як очікується, стане справжнім після будь-якого Disposeвиклику потребують доповнень в іншому випадку, непотрібний логічний прапор для кожного екземпляра багатьох видів , які могли б ігнорувати Dispose.
supercat

1
Але де б ви не викликали метод на об’єкті, який реалізує IDisposable, як ви можете перевірити, чи не було утилізовано спочатку? Замість того, щоб припустити, що це не так, і вловити виняток? Або якимось чином ви маєте намір керувати життям, щоб завжди знати, утилізується воно чи ні?
nicodemus13

3
@ nicodemus13: Як правило, не слід використовувати об'єкт, не знаючи, що він не був і не буде утилізований, за винятком випадків, коли ви готові розглядати розпорядження об'єктом зовнішнім кодом як сигнал для переривання будь-яких очікуваних дій з ним . IsDisposedПрапор може допомогти запобігти код від витрати часу на операції, не може добитися успіху, але один все ще потрібно обробляти виключення в разі , якщо об'єкт отримує розташований між IsDisposedперевіркою та спробою використати його.
supercat

WeakReferenceвидається тут актуальним. Це не зовсім IDipose'd детектор, але він говорить вам, чи це GC'd
Малахія

Відповіді:


47

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


41

System.Windows.Forms.Controlмає IsDisposedвластивість, яке має значення true після Dispose()виклику . У своїх власних об'єктах, що одноразово використовуються, ви можете легко створити подібну властивість.


OP шукав, чи є схоже властивість вже на об’єктах, які він не створює. Це було б непоганою ідеєю для об’єктів, які ми створюємо, але більшість одноразових класів у .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 як властивість і перевірити це.


2

DisposeМетод потрібно виконати всі , що очищення буде необхідно , перш ніж об'єкт припиняється; якщо не потрібно очищення, не потрібно нічого робити. Потреба в об’єкті, щоб відстежувати, чи був він утилізований, навіть коли Disposeметод інакше нічого не робить, вимагатиме від багатьох IDisposableоб’єктів додавання прапора з дуже обмеженою перевагою.

Це могло б бути корисно, якби IDisposableвключало два властивості - одне, яке вказувало, чи потребує утилізація об’єкт , і одне, яке вказувало на те, що об’єкт не був зроблений марним при утилізації. Для об'єктів, де розпорядження насправді щось робить, спочатку обидва значення були б істинними, а після стали б хибними Dispose. Для об'єктів, де утилізація не потребує очищення, перший метод завжди може повернути false, а другий завжди true, без необхідності зберігати прапор де-небудь. Однак я не думаю, що зараз їх можна додати до .NET.


ІМХО, два прапори надмірні. Я думаю, що краще дотримуватися звичної парадигми, коли кожен має єдиний прапор, як тільки Dispose було викликано на об’єкті. В іншому випадку ви додаєте складності, просто знаючи, що певні об'єкти "все ще корисні", навіть якщо на них було викликано Dispose. Йти цією дорогою не варто.
ToolmakerSteve

@ToolmakerSteve: Як правило, не буде нуля або одного прапора. Для об'єктів, які потребують утилізації, властивості "потребує розпорядження" та "є корисним" дадуть "true / true" перед тим, як розпорядитися, та "false / false" після цього, але для об'єктів, де утилізація буде забороною, обидва будуть безумовно повернути "false / true". Сказати, що об’єкт все ще потребує утилізації, коли цього ніколи не робить, або що об’єкт не є корисним, коли він завжди є, було б досить хитко. Я припускаю, що іншим підходом було б використання перерахованого типу, щоб вказати, чи потрібен тип утилізації, чи був утилізований, чи просто йому все одно.
суперкіт

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

1

Я бачу, це старе, але я не бачив відповіді. Деякі не всі одноразові об'єкти, такі як набір даних, мають подію, яку ви можете приєднати.

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

Добре знати. Зокрема, Disposedevent є членом 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

3
Чому ви робите все це замість того, щоб використовувати Usingтвердження? Це, безумовно, існувало ще в 2013 році, коли була написана така відповідь.
Коді Грей

Дійсно "GoodExit:" що це за 1983 рік для GOTO ?? Будь ласка, припиніть користуватися цим.
Мойсей

Це не відповідає на питання. Зокрема, після того, як inputPdfбуло встановлено значення (крім Нічого), ваша відповідь не показує жодного способу дізнатися, чи inputPdfбуло утилізовано. Ви могли б частково вирішити цю проблему, встановивши inputPdf = Nothingпісля утилізації. Однак це не допомогло б будь-яким іншим змінним, які були вказані на той самий об'єкт, що і inputPdf. Тобто , якщо ви робите: inputPdf = New PdfReader, Dim pdf2 As PdfReader = inputPdf, inputPdf.Dispose, inputPdf = Nothing, не було ще жодного способу дізнатися , що pdf2розташована (це той самий об'єкт inputPdf).
ToolmakerSteve
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.