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.
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.
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:
//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 .
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:
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 Dispose
d 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.
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();
}
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
.
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é
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.
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();
}