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 Fontlanza? 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.usingen absoluto, el GC eventualmente lo recogerá.finallybloque, no habría entrado en el bloque hasta que se hubieran construido todos los recursos.usingatry-finally, la expresión de inicialización se evalúa fuera detry. Entonces es una pregunta razonable.Respuestas:
No.
El compilador generará un
finallybloque 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
Whateverlanzamientos. Luego, elfinallybloque 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, porfoolo que nunca se completa.tryNunca ingresamos, por lo que nunca ingresamosfinallytampoco. La referencia del objeto se ha quedado huérfana. Finalmente, el GC lo descubrirá y lo pondrá en la cola del finalizador.handlesigue 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,foonunca se completa, nunca ingresamosfinallyy 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 quehandlese complete! Nuevamente, el finalizador se ejecutará, pero ahorahandlesigue 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
Fontrecurso, ¿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
lockdeclaración se han implementado así:Enterse ha escrito con mucho cuidado para que, independientemente de las excepciones que se generen ,lockEnteredse establezca en verdadero si y solo si el bloqueo se tomó realmente. Si tiene requisitos similares, entonces lo que necesita es escribir:y escriba
AllocateResourceinteligentemente comoMonitor.Enterpara que, pase lo que pase en el interiorAllocateResource,handlese 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
Blahllamadas a métodos. ¿Qué impide que suceda una ThreadAbortException en cualquiera de esos puntos?AllocateResourcepero antes de la asignación ax. AThreadAbortExceptionpuede 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
usingestá esencialmente expandido enusings 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 Fontlanza? Por lo que tengo entendido, font3 filtrará recursos y no se eliminarán".