C # me permite hacer lo siguiente (ejemplo de MSDN):
using (Font font3 = new Font("Arial", 10.0f),
font4 = new Font("Arial", 10.0f))
{
// Use font3 and font4.
}
¿Qué pasa si font4 = new Font
lanza? Por lo que entiendo, font3 filtrará recursos y no se eliminarán.
- ¿Es esto cierto? (font4 no se eliminará)
- ¿Esto significa que
using(... , ...)
debería evitarse por completo en favor del uso anidado?
c#
using
using-statement
Benjamin Gruenbaum
fuente
fuente
using(... , ...)
se compila en bloques anidados de cualquier forma, pero no lo sé con certeza.using
en absoluto, el GC eventualmente lo recogerá.finally
bloque, no habría entrado en el bloque hasta que se hubieran construido todos los recursos.using
atry
-finally
, la expresión de inicialización se evalúa fuera detry
. Entonces es una pregunta razonable.Respuestas:
No.
El compilador generará un
finally
bloque separado para cada variable.La especificación (§8.13) dice:
fuente
ACTUALIZACIÓN : utilicé esta pregunta como base para un artículo que se puede encontrar aquí ; consúltelo para una discusión adicional sobre este tema. ¡Gracias por la buena pregunta!
Aunque la respuesta de Schabse es, por supuesto, correcta y responde a la pregunta que se hizo, hay una variante importante en su pregunta que no hizo:
Déjame aclararlo un poco más. Supongamos que tenemos:
Ahora tenemos
Esto es lo mismo que
OKAY. Supongamos
Whatever
lanzamientos. Luego, elfinally
bloque se ejecuta y el recurso se desasigna. No hay problema.Supongamos
Blah1()
lanzamientos. Luego, el lanzamiento ocurre antes de que se asigne el recurso. El objeto ha sido asignado pero el ctor nunca regresa, porfoo
lo que nunca se completa.try
Nunca ingresamos, por lo que nunca ingresamosfinally
tampoco. La referencia del objeto se ha quedado huérfana. Finalmente, el GC lo descubrirá y lo pondrá en la cola del finalizador.handle
sigue siendo cero, por lo que el finalizador no hace nada. Observe que se requiere que el finalizador sea robusto frente a un objeto que se está finalizando cuyo constructor nunca se completó . Usted está obligado a escribir finalizadores que son tan fuerte. Esta es otra razón más por la que debería dejar los finalizadores de escritura a expertos y no intentar hacerlo usted mismo.Supongamos
Blah3()
lanzamientos. El lanzamiento ocurre después de que se asigna el recurso. Pero nuevamente,foo
nunca se completa, nunca ingresamosfinally
y el objeto es limpiado por el hilo del finalizador. Esta vez, el identificador no es cero y el finalizador lo limpia. De nuevo, el finalizador se ejecuta en un objeto cuyo constructor nunca tuvo éxito, pero el finalizador se ejecuta de todos modos. Obviamente debe porque esta vez, tenía trabajo que hacer.Ahora suponga que
Blah2()
lanza. ¡El lanzamiento ocurre después de que se asigna el recurso pero antes de quehandle
se complete! Nuevamente, el finalizador se ejecutará, pero ahorahandle
sigue siendo cero y ¡filtramos el controlador!Debe escribir un código extremadamente inteligente para evitar que ocurra esta fuga. Ahora, en el caso de su
Font
recurso, ¿a quién diablos le importa? Filtramos un identificador de fuente, gran cosa. Pero si necesita de manera absolutamente positiva que todos los recursos no administrados se limpien sin importar el momento de las excepciones, entonces tiene un problema muy difícil en sus manos.El CLR tiene que resolver este problema con bloqueos. Desde C # 4, los bloqueos que usan la
lock
declaración se han implementado así:Enter
se ha escrito con mucho cuidado para que, independientemente de las excepciones que se generen ,lockEntered
se establezca en verdadero si y solo si el bloqueo se tomó realmente. Si tiene requisitos similares, entonces lo que necesita es escribir:y escriba
AllocateResource
inteligentemente comoMonitor.Enter
para que, pase lo que pase en el interiorAllocateResource
,handle
se rellene si y sólo si es necesario desasignarlo.Describir las técnicas para hacerlo está más allá del alcance de esta respuesta. Consulte a un experto si tiene este requisito.
fuente
Blah
llamadas a métodos. ¿Qué impide que suceda una ThreadAbortException en cualquiera de esos puntos?AllocateResource
pero antes de la asignación ax
. AThreadAbortException
puede suceder en ese momento. Todos aquí parecen estar perdiendo mi punto, que es la creación de un recurso y la asignación de una referencia a él a una variable no es una operación atómica . Para resolver el problema que he identificado, debe convertirlo en una operación atómica.Como complemento a la respuesta de @SLaks, aquí está el IL para su código:
Tenga en cuenta los bloques de prueba / finalmente anidados.
fuente
Este código (basado en la muestra original):
Produce el siguiente CIL (en Visual Studio 2013 , dirigido a .NET 4.5.1):
Como puede ver, el
try {}
bloque no comienza hasta después de la primera asignación, que tiene lugar enIL_0012
. A primera vista, esto parece asignar el primer elemento en un código desprotegido. Sin embargo, observe que el resultado se almacena en la ubicación 0. Si la segunda asignación falla, el bloque externofinally {}
se ejecuta, y este obtiene el objeto de la ubicación 0, es decir, la primera asignación defont3
, y llama a suDispose()
método.Curiosamente, la descompilación de este ensamblado con dotPeek produce la siguiente fuente reconstituida:
El código descompilado confirma que todo es correcto y que
using
está esencialmente expandido enusing
s anidados . El código CIL es un poco confuso de ver, y tuve que mirarlo durante unos minutos antes de entender correctamente lo que estaba sucediendo, por lo que no me sorprende que algunos 'cuentos de viejas' hayan comenzado a brotar sobre esta. Sin embargo, el código generado es la verdad inexpugnable.fuente
Aquí hay un código de muestra para probar la respuesta de @SLaks:
fuente
font4 = new Font
lanza? Por lo que tengo entendido, font3 filtrará recursos y no se eliminarán".