2009-06-03 15 views
22

me he encontrado ildasm para encontrar que esto:curioso C# expansión mediante declaración

using(Simple simp = new Simple()) 
    { 
     Console.WriteLine("here"); 
    } 

genera código IL que es equivalente a esto:

Simple simp = new Simple(); 
    try 
    { 
     Console.WriteLine("here"); 
    } 
    finally 
    { 
     if(simp != null) 
     { 
      simp.Dispose(); 
     } 
    } 

y la pregunta es ¿por qué diablos lo hace comprobar nulo en el finalmente? El bloque finally solo se ejecutará si se ejecuta el bloque try, y el bloque try solo se ejecutará si el constructor Simple tiene éxito (es decir, no lanza una excepción), en cuyo caso simp será no nulo. (Si hay algún temor de que algunos pasos intermedios puedan venir entre el constructor Simple y el comienzo del bloque try, entonces eso sería realmente un problema porque entonces podría lanzarse una excepción que evitaría que el bloque finally se ejecute). Entonces, ¿por qué demonios?

Dejando a un lado (por favor) el argumento de si la instrucción using es mejor que tratar, por último, escribo mis try-finally bloques como:

Simple simp = new Simple(); 
    try 
    { 
     Console.WriteLine("here"); 
    } 
    finally 
    { 
     simp.Dispose(); 
     simp = null;  // sanity-check in case I touch simp again 
          // because I don't rely on all classes 
          // necessarily throwing 
          // ObjectDisposedException 
    } 
+0

Tengo curiosidad por una cosa: cómo "caro" es ese control de cordura adicional (simp = null) en comparación con el control de cordura generado por el compilador, en términos de rendimiento? Al final, la diferencia entre esos dos parece más filosófica que práctica, pero puedo estar equivocado. Debate interesante de cualquier manera. –

+0

@Fredrik - ¿Entonces está preguntando si "establecer en nulo" es más rápido/más lento que "comparar con nulo"? No estoy seguro.Aparte de eso, un beneficio de la sentencia using es que no tiene que preocuparse de que se acceda a ese objeto fuera del ámbito de uso. (A menos que tenga otra referencia). – dss539

+22

"¿Por qué demonios se anota nulo en el final?" No hay una buena razón. Saltarse la verificación nula es una optimización que podríamos haber realizado. Nosotros no. No realmente un gran acuerdo; los controles nulos son cortos y baratos. –

Respuesta

23

No, siempre se ejecutará el bloque finally. Es posible que no obtenga el objeto de una función nueva, sino de otra función que devuelve su objeto, y puede devolver NULO. using() es tu amigo!

dss539 fue tan amable de sugerir incluyo su nota:

using(Simple simp = null) 

es otra razón por la que la expansión debe comprobar primero nula.

+2

Solo para expandir un poco, usted podría estar usando Factory Pattern y haciendo algo como usar (myFactory.CreateMyObject()) que podría dar como resultado un objeto nulo. –

+8

Si el constructor falla (antes de la prueba), finalmente no se ejecutará. – Zifre

+9

La suposición es que el 'nuevo' arrojará una excepción si falla, y nunca devolverá un nulo. Si lanza la excepción, ni siquiera se alcanza el bloque try y el bloque finally no se ejecuta. Entonces, el bloque finally no se ejecuta SIEMPRE. – spoulson

10

using(Simple simp = null) es otra razón más que que primero debe comprobar la expansión para null.

+0

¿Por qué incluso compila? Se garantiza que es un error de tiempo de ejecución. –

+2

Solo se garantiza que será un error de tiempo de ejecución si usa el objeto simp. De acuerdo, no hay ninguna razón para usarlo si no lo hace .... –

+1

Y aunque es poco probable que codifique = nulo explícitamente, podría ser = SomeFactoryMethod() que podría devolver nulo. – n8wrl

4

MSDN en la instrucción using.

Lo que me parece extraño es que no se expande a:

Simple simp = new Simple(); 
Simple __compilergeneratedtmpname = simp; 
try 
{ 
    Console.WriteLine("here"); 
} 
finally 
{ 
    if(__compilergeneratedtmpname != null) 
    { 
     __compilergeneratedtmpname.Dispose(); 
    } 
} 
+0

¿Le gustaría poder cambiar la variable 'simp' dentro del bloque de uso? –

+0

No me di cuenta de que el compilador impone el simp y el readonly efectivamente. – Dolphin

+3

Dolphin: Eso causaría problemas cuando se multiplique. Cuando el constructor es seguido inmediatamente por el intento, el CLR garantiza que ningún hilo "interrumpa" en el medio. A tu manera, no hay garantía. – configurator

1

Parece que tu comentario:

"Si hay algún temor de que algunos pasos intermedios podrían venir entre la simple constructor y el comienzo del bloque try, entonces eso realmente sería un problema porque entonces podría lanzarse una excepción que evitaría que el bloque finally se ejecute en absoluto ".

es posible que esté muerto. Ver:

Atomicity & Asynchronous Exception Failures

También quiero destacar el tema (s) con WCF y usando:

Avoiding Problems with the Using Statement and WCF Service Proxies cuales referencias:

Avoiding Problems with the Using Statement