ritornando nel mezzo di un blocco usando


196

Qualcosa di simile a:

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

Credo che non sia un posto adatto per una dichiarazione di ritorno, vero?

Risposte:


194

Come molti altri hanno sottolineato in generale, questo non è un problema.

L'unico caso che causerà problemi è se si ritorna nel mezzo di un'istruzione using e si restituisce inoltre la variabile in using. Ma, di nuovo, ciò causerebbe problemi anche se non si è tornati e si è semplicemente tenuto un riferimento a una variabile.

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

Altrettanto male

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

1
Stavo per modificare la mia domanda sul punto che hai citato. Grazie.
Tafa,

Per favore, aiutami a capire perché questo è negativo. Vorrei restituire uno Stream che sto utilizzando in una funzione di supporto a un'altra funzione per l'elaborazione delle immagini. Sembra che lo Stream verrà eliminato se lo faccio?
John Shedletsky,

3
@JohnShedletsky In questo caso la chiamata di funzione deve essere completata con l'utilizzo. Come usare (Stream x = FuncToReturnStream ()) {...} e non usare dentro FuncToReturnStream.
Felix Keil,

@JohnShedletsky Sono sicuro che è perché l' returnistruzione rende la fine del usingblocco inaccessibile da qualsiasi percorso del codice. La fine del usingblocco deve essere eseguita in modo che l'oggetto possa essere disposto, se necessario.
facepalm42,

147

Va benissimo.

Apparentemente lo stai pensando

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

è tradotto alla cieca in:

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

Il che, è vero, sarebbe un problema e renderebbe la usingfrase piuttosto inutile --- ed è per questo che non è quello che fa.

Il compilatore si assicura che l'oggetto sia disposto prima che il controllo lasci il blocco, indipendentemente da come lascia il blocco.


7
A quanto pare, lo ero.
Tafa,

Ottima risposta @James Curran! Ma mi rende piuttosto curioso in cosa viene tradotto. O è espressibile solo in IL? (che non ho mai provato a leggere prima).
Bart,

1
@Bart - Penso a come valutare l'espressione di ritorno in una variabile temporanea, quindi fare lo smaltimento, quindi restituire la variabile temporanea.
ToolmakerSteve

@James Curran. Dall'alto a qui, solo tu hai spiegato cosa è successo in background. Grazie molto.
Sercan Timoçin,

@Bart è probabilmente tradotto in: prova {... il tuo codice ...} finalmente {x.Dispose (); }
Bip901

94

Va assolutamente bene, nessun problema. Perché credi che sia sbagliato?

Un'istruzione using è solo zucchero sintattico per un blocco try / finally, e come dice Grzenio va bene tornare anche da un blocco try.

L'espressione di ritorno verrà valutata, quindi verrà eseguito il blocco finally, quindi verrà restituito il metodo.


5
La risposta di James Curran spiega cosa stavo pensando.
Tafa,

27

Funzionerà perfettamente, proprio come tornare nel mezzo di try{}finally{}


18

Questo è assolutamente accettabile. Un'istruzione using garantisce che l'oggetto IDisposable verrà eliminato, qualunque cosa accada.

Da MSDN :

L'istruzione using assicura che venga chiamato Dispose anche se si verifica un'eccezione mentre si chiamano metodi sull'oggetto. È possibile ottenere lo stesso risultato inserendo l'oggetto in un blocco try e quindi chiamando Dispose in un blocco finally; in effetti, è così che l'istruzione using viene tradotta dal compilatore.


14

Il codice seguente mostra come usingfunziona:

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());
}

Produzione:

't1' è stato creato.
't2' è stato creato.
't3' è stato creato.
Viene creato 'Creato da t1, t2, t3'.
't3' è eliminato.
't2' è eliminato.
't1' è eliminato.

Gli eliminati vengono chiamati dopo l'istruzione return ma prima dell'uscita della funzione.


1
Si noti che alcuni oggetti C # dispongono in modo personalizzato, ad esempio i client WCF sono un'istruzione using come sopra restituita "impossibile accedere all'oggetto disposto"
OzBob

-4

Forse non è vero al 100% che questo è accettabile ...

Se ti capita di annidare le usanze e di rientrare da una nidificata, potrebbe non essere sicuro.

Prendi questo come esempio:

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();
        }
    }
}

Stavo passando un DataTable per essere emesso come CSV. Con il ritorno nel mezzo, stava scrivendo tutte le righe nello stream, ma al csv emesso mancava sempre una riga (o più, a seconda della dimensione del buffer). Questo mi ha detto che qualcosa non veniva chiuso correttamente.

Il modo corretto è assicurarsi che tutti gli utilizzi precedenti siano smaltiti correttamente:

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();
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.