Por que usar try {} finalmente {} com um bloco try vazio?


239

Eu notei em System.Threading.TimerBase.Dispose() método que tem um try{} finally{}bloco, mas o try{}está vazio.

Existe algum valor em usar try{} finally{} com um vazio try?

http://labs.developerfusion.co.uk/SourceViewer/browse.aspx?assembly=SSCLI&namespace=System.Threading&type=TimerBase

[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal bool Dispose(WaitHandle notifyObject)
{
    bool status = false;
    bool bLockTaken = false;
    RuntimeHelpers.PrepareConstrainedRegions();
    try {
    }
    finally {
        do {
            if (Interlocked.CompareExchange(ref m_lock, 1, 0) == 0) {
                bLockTaken = true;
                try {
                    status = DeleteTimerNative(notifyObject.SafeWaitHandle);
                }
                finally {
                    m_lock = 0;
                }
            }
            Thread.SpinWait(1);
            // yield to processor
        }
        while (!bLockTaken);
        GC.SuppressFinalize(this);
    }

    return status;
}

System.Diagnostics.Process em torno da linha 2144 também: referenceource.microsoft.com/#System/services/monitoring/…
Patrick Artner

Respostas:


171

De http://blog.somecreativity.com/2008/04/10/the-empty-try-block-mystery/ :

Essa metodologia protege contra uma chamada Thread.Abort que interrompe o processamento. A página MSDN do Thread.Abort diz que "Finalmente os blocos não executados são executados antes que o segmento seja abortado". Portanto, para garantir que seu processamento seja finalizado, mesmo que seu thread seja interrompido no meio por alguém chamando Abort, você pode colocar todo o seu código no bloco final (a alternativa é escrever o código no bloco "catch" para determine onde você estava antes de "tentar" ser interrompido pelo Abortar e prossiga a partir daí, se desejar).



15
Porque isso não estava disponível até o .NET 2.0
Hans Passant

6
@ RobFonseca-Ensor: como Thread.BeginCriticalRegion()não impede que um thread seja abortado, ele informa ao tempo de execução que, se um thread é abortado, o estado global fica corrompido e todo o domínio do aplicativo depende de um golpe de misericórdia.
kkm

9
@ HansPassant: BeginCriticalSection()realmente não estava lá no .NET 1.x, mas não há causa e efeito que você implica em dizer porque . De fato, no .NET 1.x, mesmo um finallybloco pode ter sido interrompido por um cancelamento de thread. Esses mecanismos têm um propósito diferente: trabalhar de maneira a finallyimpedir a interrupção no meio do código, enquanto BeginCriticalSection()apenas declara no tempo de execução que o estado global está em risco.
kkm

Se o desenvolvedor tiver um tempo (verdadeiro) finalmente, o abortamento será concluído ou um abortamento tecnicamente pode ser ignorado indefinidamente?
Max Jovem

64

Isso é para evitar a Thread.Abortinterrupção de um processo. A documentação para este método diz que:

Finalmente, blocos não executados são executados antes que o encadeamento seja abortado.

Isso ocorre porque, para recuperar com êxito de um erro, seu código precisará ser limpo depois de si mesmo. Desde C # não tem C ++ - destruidores de estilo, finallye usingblocos são a única maneira confiável de garantir que tal limpeza é realizada de forma confiável. Lembre-se de que o usingbloco se transforma nisso pelo compilador:

try {
    ...
}
finally {
    if(obj != null)
        ((IDisposable)obj).Dispose();
}

No .NET 1.x, havia uma chance de o finallybloco ser abortado. Esse comportamento foi alterado no .NET 2.0.

Além disso, os tryblocos vazios nunca são otimizados pelo compilador.


Obrigado pela compreensão do bloco de uso.
Stefan

@Anton Entendo que usar é uma prática recomendada. Mas, para fins de zombaria, às vezes uma classe de wrapper precisa ser implementada e o objeto descartável se torna uma variável de classe privada. Se tornarmos esse invólucro descartável, o GC não está lidando com o descarte da variável de classe privada automaticamente?
Ozkan

@ Ozkan, o GC não descarta nada automaticamente. Você precisaria implementar um finalizador, normalmente chamando a Dispose(false);. docs.microsoft.com/en-us/dotnet/standard/garbage-collection/…
Thorarin 02/04/19

@Thorarin desculpe, mas você está errado dizendo que o GC não descarta (finaliza) automaticamente.
Ozkan
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.