Je considère cela comme un abus de la déclaration d'utilisation. Je sais que je suis minoritaire à ce poste.
Je considère que c'est un abus pour trois raisons.
Premièrement, parce que je m'attends à ce que "utiliser" soit utilisé pour utiliser une ressource et en disposer une fois que vous en avez terminé . Changer l'état du programme n'utilise pas une ressource et la modifier ne supprime rien. Par conséquent, «utiliser» pour muter et restaurer l'état est un abus; le code est trompeur pour le lecteur occasionnel.
Deuxièmement, parce que je m'attends à ce que «utiliser» soit utilisé par politesse et non par nécessité . La raison pour laquelle vous utilisez "using" pour supprimer un fichier lorsque vous en avez terminé n'est pas parce qu'il est nécessaire de le faire, mais parce que c'est poli - quelqu'un d'autre attend peut-être pour utiliser ce fichier, en disant "terminé maintenant "est la chose moralement correcte à faire. J'espère que je devrais être en mesure de refactoriser une "utilisation" de sorte que la ressource utilisée soit conservée plus longtemps, et éliminée plus tard, et que le seul impact de le faire soit de gêner légèrement les autres processus . Un bloc "using" qui a un impact sémantique sur l'état du programme est abusif parce qu'il cache une mutation importante et nécessaire de l'état du programme dans une construction qui semble être là pour la commodité et la politesse, pas pour la nécessité.
Et troisièmement, les actions de votre programme sont déterminées par son état; la nécessité d'une manipulation soigneuse de l'état est précisément la raison pour laquelle nous avons cette conversation en premier lieu. Voyons comment nous pourrions analyser votre programme original.
Étiez-vous pour apporter ceci à une révision de code dans mon bureau, la première question que je poserais est "est-il vraiment correct de verrouiller le frobble si une exception est levée?" Il est manifestement évident à partir de votre programme que cette chose re-verrouille agressivement le frobble quoi qu'il arrive. Est-ce correct? Une exception a été levée. Le programme est dans un état inconnu. Nous ne savons pas si Foo, Fiddle ou Bar ont jeté, pourquoi ils ont jeté, ou quelles mutations ils ont effectuées dans un autre état qui n'ont pas été nettoyés. Pouvez-vous me convaincre que dans cette terrible situation, c'est toujours la bonne chose à faire de refermer?
C'est peut-être le cas, peut-être que ce n'est pas le cas. Ce que je veux dire, c'est qu'avec le code tel qu'il a été écrit à l'origine, le réviseur de code sait poser la question . Avec le code qui utilise "using", je ne sais pas poser la question; Je suppose que le bloc "using" alloue une ressource, l'utilise un peu et s'en débarrasse poliment quand il est fait, pas que l' accolade fermante du bloc "using" mute l'état de mon programme dans une circonstance exceptionnelle quand arbitrairement plusieurs les conditions de cohérence de l'état du programme ont été violées.
L'utilisation du bloc "using" pour avoir un effet sémantique rend ce programme fragmenté:
}
extrêmement significatif. Quand je regarde cette attelle simple, je ne pense pas immédiatement que "cette attelle a des effets secondaires qui ont des répercussions profondes sur l'état global de mon programme". Mais quand vous abusez de "l'utilisation" comme ça, tout à coup, c'est le cas.
La deuxième chose que je demanderais si j'ai vu votre code original est "que se passe-t-il si une exception est levée après le déverrouillage mais avant que l'essai ne soit entré?" Si vous exécutez un assembly non optimisé, le compilateur a peut-être inséré une instruction no-op avant l'essai, et il est possible qu'une exception d'abandon de thread se produise sur le no-op. C'est rare, mais cela se produit dans la vraie vie, en particulier sur les serveurs Web. Dans ce cas, le déverrouillage se produit mais le verrouillage ne se produit jamais, car l'exception a été levée avant l'essai. Il est tout à fait possible que ce code soit vulnérable à ce problème et qu'il doive en fait être écrit
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();
}
Encore une fois, peut-être que oui, peut-être pas, mais je sais poser la question . Avec la version "using", il est sensible au même problème: une exception d'abandon de thread pourrait être levée après que le Frobble soit verrouillé mais avant que la région try-protected associée à l'utilisation ne soit entrée. Mais avec la version "using", je suppose que c'est un "et alors?" situation. C'est malheureux si cela se produit, mais je suppose que l '«utilisation» n'est là que pour être poli, pas pour muter un état de programme d'une importance vitale. Je suppose que si une terrible exception d'abandon de thread se produit exactement au mauvais moment, alors, eh bien, le garbage collector nettoiera finalement cette ressource en exécutant le finaliseur.