66 votes

Existe-t-il une situation dans laquelle Dispose ne sera pas appelé pour un bloc 'using'?

C'était une entrevue téléphonique à la question que j'ai eu: Est-il un moment où Jeter ne sera pas appelée sur un objet qui a une portée déclarée par un à l'aide de bloc?

Ma réponse a été négative, même si une exception se produit lors de l'utilisation de bloc, Jetez sera toujours appelé.

L'interviewer s'y est opposée et a dit que si using est enveloppé dans un try-catch bloc puis les Disposer ne sera pas appelé au moment où vous entrez dans le bloc catch.

C'est contraire à ma compréhension de la construction, et je n'ai pas été en mesure de trouver quelque chose qui sauvegarde les enquêteurs point de vue. Est-il correct ou dois-je mal compris la question?

58voto

Øyvind Bråthen Points 25211

Quatre choses qui seront la cause de Disposer de ne pas être appelée dans un bloc using:

  1. Une panne sur votre machine lorsque l'intérieur de la à l'aide de bloc.
  2. Votre machine se fondre en une bombe atomique, alors que dans le l'intérieur de la à l'aide de bloc.
  3. Part insaisissable des exceptions comme StackOverflowException, AccessViolationException et peut-être d'autres.
  4. De l'environnement.FailFast

53voto

VdesmedT Points 6831
 void Main()
{
    try
    {
        using(var d = new MyDisposable())
        {
            throw new Exception("Hello");
        }
    }
    catch
    {
        "Exception caught.".Dump();
    }

}

class MyDisposable : IDisposable
{
    public void Dispose()
    {
        "Disposed".Dump();
    }
}
 

Cela a produit:

 Disposed
Exception caught
 

Je suis donc d'accord avec vous et non avec l'intervieweur smarty ...

25voto

Ian Points 13892

Bizarrement, j'ai lu environ une circonstance où Départir de ne pas être appelé dans un bloc using ce matin. La caisse de ce blog sur MSDN. C'est autour de l'aide de Disposer de IEnumerable et le mot clé yield, lorsque vous n'avez pas de réitérer la totalité de la collection.

Malheureusement, cela n'a pas affaire à des cas d'exception, honnêtement, je ne suis pas sûr que l'un. Je me serais attendu à être fait, mais c'est peut-être la peine de vérifier rapide avec un peu de code?

21voto

svick Points 81772

Les autres réponses au sujet de la panne de courant, Environment.FailFast(), les itérateurs ou de la tricherie en using quelque chose, c'est - null sont tous intéressants. Mais je trouve curieux que personne n'a mentionné ce que je pense est la situation la plus courante lors de l' Dispose() ne sera pas appelé, même en présence d' using: lorsque l'expression à l'intérieur d' using déclenche une exception.

Bien sûr, ce qui est logique: l'expression en using a déclenché une exception, si l'affectation n'a pas lieu et il n'y a rien que l'on pourrait appeler Dispose() sur. Mais le jetable objet peut déjà exister, même si elle peut être en demi-état initialisé. Et même dans cet état, il peut déjà tenir certaines ressources non managées. C'est une autre raison pourquoi mettre correctement en œuvre le modèle jetable est important.

Exemple de code posant problème:

using (var f = new Foo())
{
    // something
}

…

class Foo : IDisposable
{
    UnmanagedResource m_resource;

    public Foo()
    {
        // obtain m_resource

        throw new Exception();
    }

    public void Dispose()
    {
        // release m_resource
    }
}

Ici, on dirait qu' Foo communiqués m_resource correctement et nous utilisons using correctement. Mais l' Dispose() sur Foo n'est jamais appelée, en raison de l'exception. La solution dans ce cas est d'utiliser l'outil de finalisation et de la libération de la ressource, il y a trop.

19voto

cHao Points 42294

L' using bloc est transformé par le compilateur en try/finally bloc de son propre, au sein de l'existant, try bloc.

Par exemple:

try 
{
    using (MemoryStream ms = new MemoryStream())
        throw new Exception();
}
catch (Exception)
{
    throw;
}

devient

.try
{
  IL_0000:  newobj     instance void [mscorlib]System.IO.MemoryStream::.ctor()
  IL_0005:  stloc.0
  .try
  {
    IL_0006:  newobj     instance void [mscorlib]System.Exception::.ctor()
    IL_000b:  throw
  }  // end .try
  finally
  {
    IL_000c:  ldloc.0
    IL_000d:  brfalse.s  IL_0015
    IL_000f:  ldloc.0
    IL_0010:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
    IL_0015:  endfinally
  }  // end handler
}  // end .try
catch [mscorlib]System.Exception 
{
  IL_0016:  pop
  IL_0017:  rethrow
}  // end handler

Le compilateur ne pas réorganiser les choses. Ça se passe donc comme ceci:

  1. Exception est levée dans, ou se propage à, de la using block try partie
  2. Contrôle des feuilles de l' using block try part, et entre dans sa finally partie
  3. L'objet est disposé par le code de l' finally bloc
  4. Contrôle des feuilles du bloc finally, et l'exception est propagée à l'extérieur try
  5. Contrôle des feuilles extérieures try et va dans le gestionnaire d'exception

Point de l'être, à l'intérieur finally bloc s'exécute toujours avant que l'extérieur de la catch, parce que l'exception ne doit pas se propager jusqu'à l' finally bloc de finitions.

Le seul cas où cela n'arrivera pas, est un générateur de (excusez-moi, "itérateur"). Un itérateur est transformé en un semi-complexes de l'état de la machine, et finally blocs ne sont pas garantis à exécuter si elle devient inaccessible après une yield return (mais avant qu'il ait été éliminés).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X