2010-04-08 10 views
11

En la mayoría de los ejemplos que se encuentran en la web cuando explícitamente no usar "usando", el patrón se ve algo como:IDisposable: ¿es necesario verificar null en finally {}?

SqlConnection c = new SqlConnection(@"..."); 
    try { 
    c.Open(); 
    ... 
    } finally { 
    if (c != null) //<== check for null 
     c.Dispose(); 
    } 

Si utiliza el "uso" y mirar el código IL generado, puede ver que se genera el cheque por nula

L_0024: ldloc.1 
L_0025: ldnull 
L_0026: ceq 
L_0028: stloc.s CS$4$0000 
L_002a: ldloc.s CS$4$0000 
L_002c: brtrue.s L_0035 
L_002e: ldloc.1 
L_002f: callvirt instance void [mscorlib]System.IDisposable::Dispose() 
L_0034: nop 
L_0035: endfinally 

entiendo por qué la IL se traduce para comprobar nula (no sabe lo que hizo en el interior del bloque de usar), pero si usted está utilizando try .. finalmente y usted tiene el control total de cómo el objeto IDisposable se usa dentro del intento ... por fin bl ock, ¿realmente necesitas verificar null? si es así, ¿por qué?

+4

¿Podría ser para salvaguardar contra una excepción de referencia nula en el caso de que establezca la variable como nula dentro del bloque using/try? – Gishu

+0

eso es lo que estoy pensando – BlackTigerX

+0

Buena pregunta ... me hizo sacar mi teclado de códigos y aprender algo nuevo. – Gishu

Respuesta

12

"mediante" declaraciones puede inicializar variables con llamadas que no sean constructores. Por ejemplo:

using (Foo f = GetFoo()) 
{ 
    ... 
} 

Aquí f podría fácilmente ser nulos - mientras que una llamada al constructor nunca puede devuelto nulo. Es por eso que una declaración using comprueba la nulidad. No tiene que ver con lo que está dentro del bloque, porque la declaración using conserva el valor inicial original. Si escribe:

Stream s; 
using (s = File.OpenRead("foo.txt")) 
{ 
    s = null; 
} 

, la corriente seguirá siendo eliminada. (Si la variable es declaró en la parte de inicialización de la declaración using, es de sólo lectura de todos modos.)

En su caso, como saben que c es no nulo antes de entrar en el bloque try, que Don No es necesario que verifique nulo en el bloque finally a menos que esté reasignando su valor (¡que sinceramente espero que no sea así!) dentro del bloque.

ahora con su código actual no es el ligero riesgo de que una excepción asíncrona podría ser lanzado entre la asignación a c y entrar en el bloque try - pero es difícil de evitar este tipo de condición de carrera por completo, ya que podría igualmente ser una excepción asincrónica después de que el constructor haya completado, pero antes de que el valor se asigne a c en absoluto. Sugeriría que la mayoría de los desarrolladores no necesitan preocuparse por este tipo de cosas: las excepciones asincrónicas tienden a ser lo suficientemente "duras" como para que de todos modos reduzcan el proceso.

¿Hay alguna razón por la que no quiera usar una declaración de uso de todos modos? Para ser honesto, rara vez escribo mis propios bloques finally en estos días ...


respuesta y llorar de ver a Marc. Sin embargo, usualmente no es relevante.

+0

sí, hay un motivo en este caso específico para * no * usar una instrucción de uso, normalmente uso y no uso preocúpate – BlackTigerX

+0

mi caso específico crea una instancia de SqlConnection, o toma una instancia en vivo y la usa, al final tengo que deshacerme de ella en caso de que no fuera una "instancia en vivo" – BlackTigerX

+0

"En tu caso, como sabes esa c no es nula antes de ingresar al bloque try, no es necesario que verifique null en el bloque finally a menos que esté reasignando su valor (que espero sinceramente no lo esté) dentro del bloque ". esto responde mi pregunta, ¡gracias! – BlackTigerX

5

En su ejemplo, la comprobación de nulo no es necesaria, ya que c no puede ser nulo después de la construcción explícita.

En el siguiente ejemplo (similar a lo que se genera por el uso de declaración), un cheque por nula sería por supuesto necesario:

SqlConnection c = null; 
    try { 
    c = new SqlConnection(@"..."); 
    c.Open(); 
    ... 
    } finally { 
    if (c != null) //<== check for null 
     c.Dispose(); 
    } 

Además, un cheque por nula sería necesaria en el siguiente caso, como no se puede estar seguro de CreateConnection no volverá nulo:

SqlConnection c = CreateConnection(...); 
    try { 
    c.Open(); 
    ... 
    } finally { 
    if (c != null) //<== check for null 
     c.Dispose(); 
    } 
+0

No sabe que la parte "..." de su código no es "c = null"; entonces el cheque sería necesario. –

+0

@Matt - cierto, pero supuestamente sabe lo que hay allí. – Joe

+0

Mi sensación es que, debido a que está leyendo esto a partir de ejemplos de código, probablemente también tengan el "..." y los autores no se arriesgan. –

2

Jon ya cubrió el punto principal aquí ... pero solo una pieza aleatoria de curiosidades; su constructor puede devolver null. No es realmente un caso común, y ciertamente no es la verdadera razón por la que using hace esto, y usted realmente no debería torturar su código para esto, pero it can happen (busque MyFunnyProxy).

+0

¿no lanzaría una excepción si no fuera capaz de crear una instancia de SqlConnection? – BlackTigerX

+0

verifique el comentario de Jon: "mientras que una llamada de constructor nunca puede devolver nulo" – BlackTigerX

+0

@BlackTigerX: Sí, necesito editar eso :) –

0

Interesante .. Estoy usando VS2010 y lo encuentro a través de mis fragmentos de código personalizados (R :) - usando bloques rock.

  • En un bloque de uso, no puede asignar nulo (o cualquier cosa para el caso) a la variable de bloque. Resulta en un compiler error CS1656
  • Después de asignar el bloque var a través de un método de fábrica, que devuelve nulo. En este caso, un bloque de uso vacío inteligentemente no llama a Dispose. (Obviamente, obtendría una NullReferenceException si intenta utilizar el bloque var)
  • A continuación, en un bloque try, no hay reglas, puede asignar nulo a la variable. Por lo tanto, una verificación nula antes de Dispose es obligatoria en el bloque finally.