Considero que esto es un abuso de la declaración de uso. Soy consciente de que soy una minoría en este puesto.
Considero que esto es un abuso por tres razones.
Primero, porque espero que "usar" se use para usar un recurso y desecharlo cuando haya terminado con él . Cambiar el estado del programa no significa utilizar un recurso y volver a cambiarlo no significa eliminar nada. Por lo tanto, "usar" para mutar y restaurar el estado es un abuso; el código es engañoso para el lector casual.
En segundo lugar, porque espero que "using" se use por cortesía, no por necesidad . La razón por la que usa "using" para deshacerse de un archivo cuando haya terminado con él no es porque sea necesario hacerlo, sino porque es educado - alguien más podría estar esperando para usar ese archivo, entonces diciendo "listo ahora "es lo moralmente correcto. Espero poder refactorizar un "uso" para que el recurso usado se conserve durante más tiempo y se elimine más tarde, y que el único impacto de hacerlo sea incomodar levemente otros procesos . Un bloque "en uso" que tiene un impacto semántico en el estado del programa. es abusivo porque oculta una mutación importante y requerida del estado del programa en una construcción que parece que está ahí por conveniencia y cortesía, no por necesidad.
Y tercero, las acciones de su programa están determinadas por su estado; la necesidad de una manipulación cuidadosa del estado es precisamente la razón por la que estamos teniendo esta conversación en primer lugar. Consideremos cómo podríamos analizar su programa original.
Si tuviera que llevar esto a una revisión de código en mi oficina, la primera pregunta que haría es "¿es realmente correcto bloquear el frobble si se lanza una excepción?" Es descaradamente obvio por su programa que esta cosa vuelve a bloquear agresivamente el frobble sin importar lo que suceda. ¿Está bien? Se ha lanzado una excepción. El programa se encuentra en un estado desconocido. No sabemos si Foo, Fiddle o Bar arrojaron, por qué lanzaron o qué mutaciones realizaron a otro estado que no se limpiaron. ¿Puedes convencerme de que en esa terrible situación siempre es lo correcto volver a bloquear?
Quizás lo sea, quizás no lo sea. Mi punto es que con el código tal como se escribió originalmente, el revisor del código sabe hacer la pregunta . Con el código que usa "usar", no sé hacer la pregunta; Supongo que el bloque "using" asigna un recurso, lo usa por un tiempo y lo desecha cortésmente cuando está hecho, no que la llave de cierre del bloque "using" mute el estado de mi programa en una circunstancia excepcional cuando arbitrariamente muchos se han violado las condiciones de coherencia del estado del programa.
El uso del bloque "using" para tener un efecto semántico hace que este programa se fragmente:
}
extremadamente significativo. Cuando miro ese único aparato ortopédico, no pienso de inmediato que "el aparato ortopédico tiene efectos secundarios que tienen un impacto de gran alcance en el estado global de mi programa". Pero cuando abusa del "uso" de esta manera, de repente lo hace.
La segunda cosa que preguntaría si viera su código original es "¿qué sucede si se lanza una excepción después del desbloqueo pero antes de que se ingrese el intento?" Si está ejecutando un ensamblado no optimizado, es posible que el compilador haya insertado una instrucción de no operación antes del intento, y es posible que se produzca una excepción de aborto de subproceso en la no operación. Esto es raro, pero sucede en la vida real, particularmente en servidores web. En ese caso, el desbloqueo ocurre pero el bloqueo nunca ocurre, porque la excepción se lanzó antes del intento. Es muy posible que este código sea vulnerable a este problema y, en realidad, debería escribirse
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();
}
Una vez más, tal vez lo haga, tal vez no, pero sé hacer la pregunta . Con la versión "using", es susceptible al mismo problema: se podría lanzar una excepción de aborto de hilo después de que Frobble esté bloqueado pero antes de que se ingrese la región protegida por prueba asociada con el using. Pero con la versión de "uso", supongo que se trata de un "¿y qué?" situación. Es una lástima que eso suceda, pero supongo que el "uso" es solo para ser educado, no para cambiar el estado del programa de vital importancia. Supongo que si ocurre una terrible excepción de aborto de subprocesos exactamente en el momento equivocado, entonces, bueno, el recolector de basura limpiará ese recurso eventualmente ejecutando el finalizador.