Eu considero isso um abuso da declaração de uso. Estou ciente de que sou minoria nesta posição.
Considero isso um abuso por três razões.
Primeiro, porque espero que "usando" seja usado para usar um recurso e descartá-lo quando terminar de usá- lo . Alterar o estado do programa não é usar um recurso e alterá-lo de volta não significa descartar nada. Portanto, "usar" para alterar e restaurar o estado é um abuso; o código é enganoso para o leitor casual.
Em segundo lugar, porque espero que "usar" seja usado por educação, não por necessidade . A razão de você usar "usar" para descartar um arquivo quando terminar de usá-lo não é porque é necessário fazer isso, mas porque é educado - outra pessoa pode estar esperando para usar esse arquivo, dizendo "pronto agora "é a coisa moralmente correta a fazer. Espero ser capaz de refatorar um "uso" de modo que o recurso usado seja mantido por mais tempo e descartado mais tarde, e que o único impacto disso seja incomodar um pouco outros processos . Um bloco "usando" que tem impacto semântico no estado do programa é abusivo porque esconde uma mutação importante e necessária do estado do programa em uma construção que parece estar lá por conveniência e educação, não por necessidade.
E terceiro, as ações de seu programa são determinadas por seu estado; a necessidade de manipulação cuidadosa do estado é exatamente o motivo pelo qual estamos tendo essa conversa em primeiro lugar. Vamos considerar como podemos analisar seu programa original.
Se você trouxesse isso para uma revisão de código em meu escritório, a primeira pergunta que eu faria é "é realmente correto bloquear o frobble se uma exceção for lançada?" É flagrantemente óbvio a partir do seu programa que essa coisa bloqueia agressivamente o frobble, não importa o que aconteça. Isso está certo? Uma exceção foi lançada. O programa está em um estado desconhecido. Não sabemos se Foo, Fiddle ou Bar jogou, por que jogou, ou quais mutações eles executaram em outro estado que não foram limpos. Você pode me convencer de que nessa situação terrível é sempre a coisa certa a fazer para travar novamente?
Talvez seja, talvez não. Meu ponto é que, com o código como foi originalmente escrito, o revisor sabe fazer a pergunta . Com o código que usa "using", não sei como fazer a pergunta; Eu suponho que o bloco "usando" aloca um recurso, usa-o por um tempo e o descarta educadamente quando é feito, não que a chave de fechamento do bloco "usando" mude o estado do meu programa em uma circunstância excepcional quando arbitrariamente muitos as condições de consistência do estado do programa foram violadas.
O uso do bloco "usando" para ter um efeito semântico torna este fragmento de programa:
}
extremamente significativo. Quando eu olho para aquela única chave de fechamento, não penso imediatamente "que a chave tem efeitos colaterais que têm impactos de longo alcance no estado global do meu programa". Mas quando você abusa do "uso" assim, de repente acontece.
A segunda coisa que eu perguntaria se visse seu código original é "o que acontece se uma exceção for lançada após o desbloqueio, mas antes que a tentativa seja inserida?" Se você estiver executando um assembly não otimizado, o compilador pode ter inserido uma instrução no-op antes de tentar, e é possível que uma exceção de interrupção de thread ocorra no no-op. Isso é raro, mas acontece na vida real, principalmente em servidores da web. Nesse caso, o desbloqueio acontece, mas o bloqueio nunca acontece, porque a exceção foi lançada antes da tentativa. É perfeitamente possível que este código seja vulnerável a este problema e deva realmente ser escrito
bool needsLock = false;
try
{
// must be carefully written so that needsLock is set
// if and only if the unlock happened:
this.Frobble.AtomicUnlock(ref needsLock);
blah blah blah
}
finally
{
if (needsLock) this.Frobble.Lock();
}
Novamente, talvez sim, talvez não, mas eu sei fazer a pergunta . Com a versão "usando", é suscetível ao mesmo problema: uma exceção de anulação de thread pode ser lançada após o Frobble ser bloqueado, mas antes que a região protegida por teste associada ao uso seja inserida. Mas com a versão "usando", presumo que seja um "e daí?" situação. É lamentável se isso acontecer, mas presumo que o "uso" existe apenas para ser educado, não para alterar o estado do programa de vital importância. Presumo que, se alguma exceção terrível de aborto de thread acontecer exatamente no momento errado, então, tudo bem, o coletor de lixo limpará esse recurso eventualmente executando o finalizador.