2009-03-19 12 views
144

Quelque chose comme:retour au milieu d'un bloc à l'aide

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

Je crois que ce n'est pas un endroit approprié pour une déclaration de retour, est-il?

Répondre

149

Comme plusieurs autres l'ont souligné, ce n'est pas un problème.

Le seul cas où cela vous causera des problèmes est si vous revenez au milieu d'une instruction using et renvoyez en plus la variable in using. Mais encore une fois, cela vous causerait des problèmes même si vous ne reveniez pas et gardiez simplement une référence à une variable.

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

aussi mauvais

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

Juste j'étais sur le point de modifier ma question sur le point que vous avez mentionné. Merci. – tafa

+0

S'il vous plaît, aidez-moi à comprendre pourquoi c'est mauvais. Je voudrais retourner un flux que j'utilise dans une fonction d'aide à une autre fonction pour le traitement d'image. Il semble que le Stream sera éliminé si je fais cela? –

+1

@JohnShedletsky Dans ce cas, votre appel de fonction doit être enveloppé avec. Comme si vous utilisiez (Stream x = FuncToReturnStream()) {...} et pas d'utilisation à l'intérieur de FuncToReturnStream. –

23

Cela fonctionne parfaitement bien, tout comme le retour au milieu de try{}finally{}

14

est tout à fait acceptable que. Une instruction utilisant garantit que l'objet IDisposable sera éliminé quoi qu'il arrive.

De MSDN:

L'instruction à l'aide assure que Dispose est appelée même si une exception se produit pendant que vous appelez des méthodes sur l'objet. Vous pouvez obtenir le même résultat en plaçant l'objet dans un bloc try, puis en appelant Dispose dans un bloc finally; en fait, c'est ainsi que l'instruction using est traduite par le compilateur.

85

C'est tout à fait correct - pas de problème du tout. Pourquoi croyez-vous que c'est faux? Une instruction using est simplement du sucre syntaxique pour un bloc try/finally, et comme Grzenio dit qu'il est bon de revenir d'un bloc try aussi.

L'expression de retour sera évaluée, puis le bloc finally sera exécuté, puis la méthode retournera.

+5

La réponse de James Curran explique ce que je pensais. – tafa

96

C'est parfaitement bien.

Vous pensez apparemment que

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

est aveugle traduit en:

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

qui, il est vrai, serait un problème, et rendrait la déclaration using plutôt inutile --- qui est pourquoi c'est pas ce qu'il fait.

Le compilateur s'assure que l'objet est éliminé avant que le contrôle ne quitte le bloc, quelle que soit la manière dont il quitte le bloc.

+5

J'étais apparemment. – tafa

+0

Grande réponse @ James Curran! Mais cela me rend plutôt curieux de voir dans quoi il est traduit. Ou est-ce seulement exprimable en IL? (que je n'ai jamais vraiment essayé de lire auparavant). – Bart

+0

@Bart - Je pense à cela en évaluant l'expression de retour dans une variable temporaire, puis en effectuant la disposition, puis en retournant la variable temporaire. – ToolmakerSteve

-3

Peut-être est pas 100% vrai que cela est acceptable ...

Si vous arrive d'être usings de nidification et retour à partir d'un imbriquée , ça pourrait ne pas être sûr.

Prenez cet exemple:

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

je passais dans un DataTable être produit comme csv. Avec le retour au milieu, il écrivait toutes les lignes dans le flux, mais le csv sorti manquait toujours une ligne (ou plusieurs, selon la taille de la mémoire tampon). Cela m'a dit que quelque chose ne fermait pas correctement.

La bonne façon est de vous assurer que tous les usings précédents sont disposés correctement:

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

Le soufflet de code montre comment using fonctionne:

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

Sortie:

't1' est créé.
't2' est créé.
't3' est créé.
'Créé à partir de t1, t2, t3' est créé.
't3' est éliminé.
't2' est éliminé.
't1' est éliminé.

Les éliminés sont appelés après l'instruction return mais avant la sortie de la fonction.