2009-03-19 11 views
144

Algo así como:regresar en medio de un bloque mediante

using (IDisposable disposable = GetSomeDisposable()) 
{ 
    //..... 
    //...... 
    return Stg(); 
} 

creo que no es un lugar adecuado para una instrucción de retorno, ¿verdad?

Respuesta

149

Como muchos otros han señalado en general, esto no es un problema.

El único caso que le causará problemas es si regresa a la mitad de una instrucción de uso y, además, devuelve la variable de entrada usando. Pero, de nuevo, esto también causaría problemas incluso si no regresó y simplemente guardó una referencia a una variable.

using (var x = new Something()) { 
    // not a good idea 
    return x; 
} 

Tan malo

Something y; 
using (var x = new Something()) { 
    y = x; 
} 
+1

Solo estaba a punto de editar mi pregunta sobre el punto que mencionaste. Gracias. – tafa

+0

Por favor, ayúdame a entender por qué esto es malo. Me gustaría devolver un Stream que estoy usando en una función auxiliar a otra función para el procesamiento de imágenes. Parece que el Stream se eliminará si hago esto? –

+1

@JohnShedletsky En este caso, su llamada de función debe ser envuelta con el uso. Me gusta usar (Stream x = FuncToReturnStream()) {...} y no usar dentro de FuncToReturnStream. –

23

esto va a funcionar perfectamente bien, al igual que de regresar en medio de try{}finally{}

14

que es totalmente aceptable. Una declaración usando asegura que el objeto IDisposable será eliminado sin importar qué.

De MSDN:

La instrucción using asegura que Desechar se llama incluso si se produce una excepción, mientras que la que está llamando a métodos en el objeto. Puede lograr el mismo resultado colocando el objeto dentro de un bloque try y luego llamando a Dispose en un bloque finally; de hecho, así es como el compilador traduce la sentencia using.

85

Está absolutamente bien, no hay problema en absoluto. ¿Por qué crees que está mal?

Una declaración de uso es solo azúcar sintáctico para un bloque try/finally, y como Grzenio dice que está bien regresar también de un bloque try.

Se evaluará la expresión de retorno, luego se ejecutará el bloque finally, luego se devolverá el método.

+5

La respuesta de James Curran explica lo que estaba pensando. – tafa

96

Está perfectamente bien.

Usted está aparentemente pensando que

using (IDisposable disposable = GetSomeDisposable()) 
{ 
    //..... 
    //...... 
    return Stg(); 
} 

se traduce a ciegas en:

IDisposable disposable = GetSomeDisposable() 
//..... 
//...... 
return Stg(); 
disposable.Dispose(); 

que, sin duda, sería un problema, y ​​haría que la declaración using bastante inútil --- que es por qué eso es no qué hace.

El compilador se asegura de que el objeto se elimine antes de que el control abandone el bloque, independientemente de cómo abandone el bloque.

+5

Yo estaba, aparentemente. – tafa

+0

¡Gran respuesta @James Curran! Pero me hace bastante curioso lo que se traduce en. ¿O eso solo se puede expresar en IL? (que nunca he intentado leer antes). – Bart

+0

@Bart - Lo considero como una evaluación de la expresión de retorno en una variable temporal, luego haciendo el desecho, y luego devolviendo la variable temporal. – ToolmakerSteve

-3

Tal vez no es 100% cierto que esto es aceptable ...

Si le sucede a ser usings anidación y regresar desde el interior de una anidada , puede que no sea seguro.

tomar esto como un ejemplo:

using (var memoryStream = new MemoryStream()) 
{ 
    using (var textwriter = new StreamWriter(memoryStream)) 
    { 
     using (var csv = new CsvWriter(textwriter)) 
     { 
      //..write some stuff to the stream using the CsvWriter 
      return memoryStream.ToArray(); 
     } 
    } 
} 

yo estaba pasando en un DataTable a ser emitida como csv. Con el retorno en el medio, estaba escribiendo todas las filas en la secuencia, pero al csv de salida siempre le faltaba una fila (o múltiple, dependiendo del tamaño del búfer). Esto me dijo que algo no se cerraba correctamente.

La forma correcta es asegurarse de que todos los usings anteriores están bien dispuestos:

using (var memoryStream = new MemoryStream()) 
{ 
    using (var textwriter = new StreamWriter(memoryStream)) 
    { 
     using (var csv = new CsvWriter(textwriter)) 
     { 
      //..write some stuff to the stream using the CsvWriter 
     } 
    } 

    return memoryStream.ToArray(); 
} 
1

El fuelle de código muestra cómo using está trabajando:

private class TestClass : IDisposable 
{ 
    private readonly string id; 

    public TestClass(string id) 
    { 
     Console.WriteLine("'{0}' is created.", id); 
     this.id = id; 
    } 

    public void Dispose() 
    { 
     Console.WriteLine("'{0}' is disposed.", id); 
    } 

    public override string ToString() 
    { 
     return id; 
    } 
} 

private static TestClass TestUsingClose() 
{ 
    using (var t1 = new TestClass("t1")) 
    { 
     using (var t2 = new TestClass("t2")) 
     { 
     using (var t3 = new TestClass("t3")) 
     { 
      return new TestClass(String.Format("Created from {0}, {1}, {2}", t1, t2, t3)); 
     } 
     } 
    } 
} 

[TestMethod] 
public void Test() 
{ 
    Assert.AreEqual("Created from t1, t2, t3", TestUsingClose().ToString()); 
} 

Salida:

't1' es creado.
't2' es creado.
se crea 't3'.
'Creado desde t1, t2, t3' se crea.
't3' está dispuesto.
't2' está eliminado.
't1' está eliminado.

Los llamados se llaman después de la declaración de devolución pero antes de la salida de la función.

Cuestiones relacionadas