web-dev-qa-db-fra.com

Utilisation de l'instruction par rapport à IDisposable.Dispose ()

J'ai cru comprendre que l'instruction using dans .NET appelle la méthode Dispose() d'un objet IDisposable une fois que le code quitte le bloc.

L'instruction using fait-elle autre chose? Sinon, il semblerait que les deux exemples de code suivants obtiennent exactement la même chose:

Using Con as New Connection()
    Con.Open()
    'do whatever '
End Using

Dim Con as New Connection()
Con.Open()
'do whatever '
Con.Dispose()

Je donnerai la meilleure réponse à quiconque confirme que j'ai raison ou souligne que je me trompe et explique pourquoi. Gardez à l'esprit que je suis conscient que certaines classes peuvent faire différentes choses dans leurs méthodes Dispose(). Cette question est de savoir si l'instruction using obtient exactement le même résultat que l'appel de la méthode Dispose() d'un objet.

49
oscilatingcretin

using est fondamentalement l'équivalent de:

try
{
  // code
}
finally
{
  obj.Dispose();
}

Il a donc également l'avantage d'appeler Dispose() même si une exception non gérée est levée dans le code dans le bloc.

66
Brian Warshaw

Comme Brian Warshaw indiqué dans ici c'est simplement une implémentation des blocs try et finally pour s'assurer que l'objet est supprimé. Ajoutant à sa réponse, using block s'assure également que l'objet est supprimé même si vous retournez à l'intérieur en utilisant scope.

J'étais moi-même curieux à ce sujet et je l'ai testé en utilisant l'approche suivante:

Classe de test IDisposable personnalisée et Main

private class DisposableTest : IDisposable
{
    public string Name { get; set; }

    public void Dispose() { Console.WriteLine("{0}.Dispose() is called !", Name); }
}

public static void Main(string[] args)
{
    try
    {
        UsingReturnTest();
        UsingExceptionTest();                
    }
    catch { }

    try
    {
        DisposeReturnTest();
        DisposeExceptionTest();                
    }
    catch { }

    DisposeExtraTest();

    Console.ReadLine();
}        

Implémentation des cas de test

private static string UsingReturnTest()
{
    using (DisposableTest usingReturn = new DisposableTest() { Name = "UsingReturn" })
    {
        return usingReturn.Name;
    }
}

private static void UsingExceptionTest()
{
    using (DisposableTest usingException = new DisposableTest() { Name = "UsingException" })
    {
        int x = int.Parse("NaN");
    }
}

private static string DisposeReturnTest()
{        
    DisposableTest disposeReturn = new DisposableTest() { Name = "DisposeReturn" };
    return disposeReturn.Name;
    disposeReturn.Dispose(); // # IDE Warning; Unreachable code detected
}

private static void DisposeExceptionTest()
{
    DisposableTest disposeException = new DisposableTest() { Name = "DisposeException" };
    int x = int.Parse("NaN");
    disposeException.Dispose();
}

private static void DisposeExtraTest()
{
    DisposableTest disposeExtra = null;
    try
    {
        disposeExtra = new DisposableTest() { Name = "DisposeExtra" };
        return;
    }
    catch { }
    finally
    {
        if (disposeExtra != null) { disposeExtra.Dispose(); }
    }
}

Et la sortie est:

  • UsingReturn.Dispose () est appelé!
  • UsingException.Dispose () est appelé!
  • DisposeExtra.Dispose () est appelé!
19
Saro Taşciyan
//preceeding code
using (con = new Connection()) {
    con.Open()
    //do whatever
}
//following code

est équivalent à ce qui suit (notez la portée limitée de con):

//preceeding code
{
    var con = new Connection();
    try {
        con.Open()
        //do whatever
    } finally {
        if (con != null) con.Dispose();
    }
}
//following code

Ceci est décrit ici: http://msdn.Microsoft.com/en-us/library/yh598w02.aspx

L'instruction using garantit que Dispose est appelé 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 à l'intérieur d'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 .

9
hatchet

Une instruction using est plus claire et plus concise qu'une construction try...finally{Dispose()}, et doit être utilisée dans presque tous les cas où l'on ne veut pas autoriser la sortie d'un bloc sans Dispose être appelé. Les seules situations courantes où une élimination "manuelle" serait préférable seraient les suivantes:

  1. Une méthode appelle une méthode d'usine qui renvoie quelque chose qui peut ou non implémenter `IDisposable`, mais qui devrait être` Dispose`d si c'est le cas (un scénario qui se produit avec `IEnumerable.GetEnumerator ()` non générique). Les interfaces d'usine bien conçues doivent soit renvoyer un type qui implémente `IDisposable` (peut-être avec une implémentation de ne rien faire, comme c'est généralement le cas de` IEnumerator`), soit spécifier que les appelants ne sont pas censés `éliminer 'l'objet retourné. Malheureusement, certaines interfaces comme "IEnumerable" non générique ne répondent à aucun critère. Notez que l'on ne peut pas très bien utiliser `using 'dans de tels cas, car cela ne fonctionne qu'avec des emplacements de stockage dont le type déclaré implémente` IDisposable`.
  2. On s'attend à ce que l'objet `IDisposable` vive même après la sortie du bloc (comme c'est souvent le cas lors de la définition d'un champ` IDisposable`, ou du retour d'un `IDisposable` à partir d'une méthode d'usine).

Notez que lors du retour d'un IDisposable à partir d'une méthode d'usine, il faut utiliser quelque chose comme ceci:

 bool ok = false; 
 DisposableClass myThing; 
 essayez 
 {
 myThing = new DisposableClass (); 
 ... 
 ok = true; 
 return myThing; 
} 
 finalement 
 {
 if (! ok) 
 {
 if (myThing! = null) 
 myThing.Dispose (); 
} 
} 

pour s'assurer que myThing obtiendra Disposed s'il n'est pas retourné. Je souhaite qu'il y ait un moyen d'utiliser using avec une méthode "cancel Dispose", mais rien de tel n'existe.

6
supercat

La différence entre les deux est que, si une exception est levée

Con.Open()
'do whatever

Con.Dispose ne sera pas appelé.

Je ne suis pas sur VB syntaxe, mais en C #, le code équivalent serait

try
{
    con = new Connection();
    // Do whatever
}
finally
{
    if (con != null) con.Dispose();
}
5
Eric J.

L'instruction using garantit que l'objet est supprimé en cas de levée d'une exception. C'est l'équivalent d'appeler dispose dans un bloc finally.

3
jrummell

Using encapsule le bloc inclus dans un try/finally qui appelle Dispose dans le bloc finally. Cela garantit que Dispose sera appelé même si une exception se produit.

Vous devez utiliser en utilisant dans presque tous les cas, pour des raisons de sécurité

3
Panagiotis Kanavos

Le bloc using s'assure que Dispose() est appelée si une exception est levée.

Votre deuxième échantillon ne fait pas cela.

Si Con.Open() a levé une exception, dans le premier cas, vous êtes assuré que Con.Dispose() est appelé. Dans le second cas, l'exception se propage et Con.Dispose() ne sera pas appelée.

2
jglouie

Si la mémoire est utilisée, l'utilisation est une garantie qu'un objet est éliminé quelle que soit la façon dont le bloc de code qu'il entoure le quitte. Pour ce faire, il entoure le bloc dans un bloc try ... finally, et vérifie la nullité de la variable tilisé, puis la supprime si elle n'est pas nulle. Si une exception a été levée, il est permis de faire bouillonner la pile. En dehors de cela, il ne fait que garantir l'élimination des objets jetables non nuls.

try
{
  var myDisposable = new DisposableObject();
  myDisposable.DoSomething();
}
finally
{
  if (myDisposable != null)
    ((IDisposable)myDisposable).Dispose();
}
1
Mike Hofer