Como saber se uma referência de objeto IDisposable foi descartada?

86

Existe um método, ou alguma outra forma leve, para verificar se uma referência é a um objeto descartado?

PS - Isso é apenas uma curiosidade (durma bem, não em código de produção). Sim, eu sei que posso pegar o ObjectDisposedExceptionao tentar acessar um membro do objeto.

Neil C. Obremski
fonte
11
Não sei. Parece curioso que não haja uma bool IsDisposed { get; }declaração sobre System.IDisposable.
nicodemus13
3
@ nicodemus13: O Disposemétodo direciona um objeto para liberar todo e qualquer recurso adquirido, mas ainda não liberado. Se um objeto nunca mantém recursos, seu Disposemétodo geralmente não precisa fazer nada; se o tipo declara, void IDisposable.Dispose() {};ele pode, de outra forma, ignorar IDisposablesem sobrecarga por instância. Uma IsDisposedpropriedade que deveria se tornar verdadeira após qualquer Disposechamada necessitaria adicionar um sinalizador booleano desnecessário a cada instância de muitos tipos que poderiam ser ignorados Dispose.
supercat
1
Mas, sempre que você chamar um método em um objeto que o implementa IDisposable, como você pode verificar se ele foi descartado primeiro? Em vez de presumir que não é e capturar uma exceção? Ou, de alguma forma, você deve administrar a vida toda para que sempre saiba se ela está descartada ou não?
nicodemus13
3
@ nicodemus13: Geralmente não se deve usar um objeto sem saber que ele não foi e não será descartado, exceto nos casos em que se esteja preparado para considerar o descarte do objeto por código externo como um sinal para abortar quaisquer ações pendentes com ele . Um IsDisposedsinalizador pode ajudar a evitar que o código perca tempo em operações que possivelmente não serão bem-sucedidas, mas ainda seria necessário lidar com exceções no caso de um objeto ser descartado entre a IsDisposedverificação e a tentativa de usá-lo.
supercat de
WeakReferenceparece relevante aqui. Não é exatamente um detector IDipose, mas indica se é GC
Malaquias

Respostas:

48

Não - a implementação padrão do padrão IDisposable não o suporta

Dandikas
fonte
41

System.Windows.Forms.Controltem uma IsDisposedpropriedade que é definida como true após Dispose()ser chamada . Em seus próprios objetos IDisposable, você pode criar facilmente uma propriedade semelhante.

Ryan Lundy
fonte
O OP estava procurando ver se já existe uma propriedade semelhante nos objetos que ele não está criando. Isso seria uma boa ideia para objetos que criamos, mas a maioria das classes descartáveis ​​em .NET não segue essa convenção. A resposta de Dandikas está correta.
Krillgar
2
@krillgar, não há nada na pergunta do OP que apóie sua afirmação.
Ryan Lundy
18

Não há nada embutido que permita isso. Você precisaria expor uma propriedade booleana IsDisposed que reflete um sinalizador interno descartado.

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);
    }
}
Scott Dorman
fonte
BTW, se alguém começar a usar esse padrão, isso ajuda a definir uma nova interface ( IDisposablePlusou qualquer outra) que herda IDisposablee inclui bool IsDisposed { get; }. Isso facilita saber quais dos seus IDisposableobjetos suportam IsDisposed.
ToolmakerSteve
Não acho que você pode herdar uma interface por causa de como o C # funciona. Colocar uma interface após os dois pontos a herdam. Espero que implemente a interface por outro.
Moses
9

Se não for sua classe e não fornecer uma propriedade IsDisposed (ou algo semelhante - o nome é apenas uma convenção), você não tem como saber.

Mas se for sua classe e você estiver seguindo a implementação canônica de IDisposable , então apenas exponha o campo _disposed ou _isDisposed como uma propriedade e verifique isso.

pulo
fonte
2

O Disposemétodo é necessário para realizar qualquer limpeza necessária antes que um objeto seja abandonado; se nenhuma limpeza for necessária, não é necessário fazer nada. Exigir que um objeto rastreie se ele foi descartado, mesmo quando o Disposemétodo não faria nada de outra forma, exigiria que muitos IDisposableobjetos adicionassem um sinalizador para um benefício muito limitado.

Poderia ter sido útil IDisposableincluir duas propriedades - uma que indicava se um objeto precisava ser descartado e uma que indicava que o objeto não havia se tornado inútil pelo descarte. Para objetos em que o descarte realmente faz algo, ambos os valores seriam inicialmente verdadeiros e se tornariam falsos depois Dispose. Para objetos em que o descarte não precisa fazer nenhuma limpeza, o primeiro método sempre pode retornar falso e o segundo sempre verdadeiro, sem ter que armazenar um sinalizador em qualquer lugar. Não acho que haja alguma maneira de adicioná-los ao .NET agora.

supergato
fonte
IMHO, duas bandeiras é um exagero. Eu acho que é melhor ficar com o paradigma usual, onde se tem um único sinalizador depois que Dispose foi chamado em um objeto. Caso contrário, você adiciona complexidade, apenas para saber que certos objetos "ainda são úteis", embora Dispose tenha sido chamado para eles. Não vale a pena seguir por esse caminho.
ToolmakerSteve
@ToolmakerSteve: Geralmente, haveria zero ou um sinalizador. Para objetos que exigem descarte, as propriedades "precisa descartar" e "é útil" produziriam "verdadeiro / verdadeiro" antes de descartar e "falso / falso" depois, mas para objetos em que o descarte seria um modo autônomo, ambos seriam incondicionalmente retornar "falso / verdadeiro". Dizer que um objeto ainda precisa ser descartado quando nunca precisa, ou que um objeto não é útil quando sempre é, seria bastante desagradável. Suponho que outra abordagem seria usar um tipo enumerado para indicar se um tipo precisa ser descartado, foi descartado ou simplesmente não se importa.
supercat
@ToolmakerSteve: Acho que o grande motivo de IDisposablenão ter uma Disposedpropriedade é que teria sido percebido como estranho ter objetos para os quais a chamada Disposenão definiria tal propriedade true, mas exigindo que os objetos controlassem se Disposefoi chamado nos casos em que caso contrário, eles não teriam motivo para se preocupar, acrescentariam custos significativos e poucos benefícios.
supercat
1

Vejo que isso é antigo, mas não vi uma resposta. Alguns nem todos os objetos descartáveis, como um DataSet, têm um evento descartado que você pode anexar.

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;
    }
}
Moisés
fonte
Bom saber. Especificamente, o Disposedevento é um membro da System.ComponentModel.IComponentinterface.
ToolmakerSteve
-1

O que eu gosto de fazer é declarar os objetos sem inicializá-los, mas definir seus valores padrão para Nothing. Então, no final do loop, escrevo:

If anObject IsNot Nothing Then anObject.Dispose()

Aqui está um exemplo completo:

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

Isso também funciona muito bem para colocar seus objetos principais no topo de uma rotina, usá-los dentro de uma Tryrotina e, em seguida, descartá-los em um Finallybloco:

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
JeffreyDurham
fonte
6
@ LarsHöppner: A essência da questão é agnóstica de linguagem, e bons desenvolvedores C # provavelmente devem saber pelo menos VB.NET o suficiente para ler o código acima (e os desenvolvedores VB.NET devem aprender C # o suficiente para ler código C # que não fazer algo particularmente exótico).
supercat
3
Por que você faria tudo isso em vez de usar uma Usingdeclaração? Isso certamente existia em 2013, quando esta resposta foi escrita.
Cody Gray
Realmente "GoodExit:" o que é isso 1983 para um GOTO ?? Por favor, pare de usar isso.
Moisés,
Isso não responde à pergunta. Especificamente, depois de inputPdfter sido definido com um valor (diferente de Nothing), sua resposta não mostra como saber se inputPdffoi descartado. Você poderia resolver isso parcialmente definindo inputPdf = Nothingapós o descarte. No entanto, isso não ajudaria em nenhuma outra variável que tenha sido apontada para o mesmo objeto que inputPdf. Isto é, se você faz: inputPdf = New PdfReader, Dim pdf2 As PdfReader = inputPdf, inputPdf.Dispose, inputPdf = Nothing, ainda haveria nenhuma maneira de saber que pdf2está disposto (é o mesmo objeto inputPdf).
Toolmaker Steve