web-dev-qa-db-fra.com

Le code dans une instruction Finally sera-t-il déclenché si je renvoie une valeur dans un bloc Try?

J'examine le code d'un ami et dis qu'il utilisait une instruction return dans un bloc try-finally. Le code de la section Finally est-il toujours activé même si le reste du bloc try ne le fait pas?

Exemple:

public bool someMethod()
{
  try
  {
    return true;
    throw new Exception("test"); // doesn't seem to get executed
  }
  finally
  {
    //code in question
  }
}
212
JamesEggers

Réponse simple: oui.

241
Andrew Rollings

Normalement oui. La section finally est garantie pour exécuter quoi qu'il arrive, y compris les exceptions ou les instructions return. Une exception à cette règle est une exception asynchrone se produisant sur le thread (OutOfMemoryException, StackOverflowException).

Pour en savoir plus sur les exceptions asynchrones et le code fiable dans ces situations, consultez la rubrique régions d'exécution contraintes .

194
Mehrdad Afshari

Voici un petit test:

class Class1
{
    [STAThread]
    static void Main(string[] args)
    {
        Console.WriteLine("before");
        Console.WriteLine(test());
        Console.WriteLine("after");
    }

    static string test()
    {
        try
        {
            return "return";
        }
        finally
        {
            Console.WriteLine("finally");
        }
    }
}

Le résultat est:

before
finally
return
after
139
Jon B

Citant de MSDN 

finally est utilisé pour garantir qu'un bloc de code d'instruction est exécuté quelle que soit la manière dont le bloc try précédent est exited .

33
Perpetualcoder

Généralement oui, le enfin fonctionnera.

Pour les trois scénarios suivants, finalement TOUJOURS sera exécuté:

  1. Aucune exception ne se produit
  2. Exceptions synchrones (exceptions qui se produisent dans le flux de programme normal).
    Cela inclut les exceptions conformes à CLS qui dérivent des exceptions System.Exception et non conformes à CLS, qui ne dérivent pas de System.Exception. Les exceptions non conformes à CLS sont automatiquement encapsulées par l'exception RuntimeWrappedException. C # ne peut pas lancer d'exceptions de réclamations non CLS, mais des langages tels que C++ le peuvent. C # pourrait appeler un code écrit dans un langage capable de générer des exceptions non conformes à CLS.
  3. Asynchronous ThreadAbortException
    À partir de .NET 2.0, une exception ThreadAbortException n’empêchera plus l’exécution définitive d’une opération finale. ThreadAbortException est maintenant levé avant ou après le finally. Finally sera toujours exécuté et ne sera pas interrompu par un abandon de thread, tant que le test a été réellement entré avant que l’abandon du thread ne se soit produit.

Le scénario suivant, enfin ne fonctionnera pas:

Asynchrone StackOverflowException.
À partir de .NET 2.0, un débordement de pile entraînera la fin du processus. Enfin, l'exécution ne sera pas exécutée, sauf si une contrainte supplémentaire est appliquée pour lui permettre de devenir enfin une CER (région d'exécution contrainte). Les CER ne doivent pas être utilisés dans le code utilisateur général. Ils ne doivent être utilisés que dans les cas où il est essentiel que le code de nettoyage soit toujours exécuté - une fois que tout le processus est arrêté en cas de débordement de pile, tous les objets gérés sont donc nettoyés par défaut. Ainsi, le seul endroit où un CER devrait être pertinent concerne les ressources allouées en dehors du processus, par exemple les descripteurs non gérés.

Généralement, le code non managé est encapsulé par une classe gérée avant d'être consommé par le code utilisateur. La classe d'encapsuleur gérée utilisera généralement un SafeHandle pour envelopper le descripteur non géré. SafeHandle implémente un finaliseur critique et une méthode Release exécutée dans un CER afin de garantir l'exécution du code de nettoyage. Pour cette raison, vous ne devriez pas voir les CER générés par le code utilisateur.

Donc, le fait que finalement ne s'exécute pas sur StackOverflowException ne devrait pas avoir d'effet sur le code utilisateur, car le processus se terminera quand même. Si vous avez un cas dans lequel vous devez nettoyer une ressource non gérée, en dehors d'un SafeHandle ou d'un CriticalFinalizerObject, utilisez un CER comme suit: mais notez s'il vous plaît que c'est une mauvaise pratique - le concept non géré doit être résumé en une classe (s) gérée (s) et un (x) SafeHandle (s) approprié (s) par conception.

par exemple.,

// No code can appear after this line, before the try
RuntimeHelpers.PrepareConstrainedRegions();
try
{ 
    // This is *NOT* a CER
}
finally
{
    // This is a CER; guaranteed to run, if the try was entered, 
    // even if a StackOverflowException occurs.
}
18
markn

Il y a une exception très importante à cela que je n'ai pas vue mentionnée dans aucune autre réponse, et pour laquelle (après avoir programmé en C # pendant 18 ans), je ne peux pas croire que je ne le savais pas.

Si vous lancez ou déclenchez une exception de type any à l'intérieur de votre bloc catch (et pas seulement des noms bizarres StackOverflowExceptions et autres choses du même genre) et si vous n'avez pas le bloc entier try/catch/finally dans un autre bloc try/catch, votre finally bloc a gagné ' t exécuter. Cela est facile à démontrer - et si je ne l'avais pas vu moi-même, étant donné combien de fois j'ai lu que ce ne sont que des cas minuscules vraiment bizarres qui peuvent empêcher un bloc finally de s'exécuter, je n'y aurais pas cru.

static void Main(string[] args)
{
    Console.WriteLine("Beginning demo of how finally clause doesn't get executed");
    try
    {
        Console.WriteLine("Inside try but before exception.");
        throw new Exception("Exception #1");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Inside catch for the exception '{ex.Message}' (before throwing another exception).");
        throw;
    }
    finally
    {
        Console.WriteLine("This never gets executed, and that seems very, very wrong.");
    }

    Console.WriteLine("This never gets executed, but I wasn't expecting it to."); 
    Console.ReadLine();
}

Je suis sûr qu'il y a une raison à cela, mais il est étrange que ce ne soit pas plus connu. (C'est noté ici par exemple, mais pas n'importe où dans cette question particulière.)

9
Ken Smith

Je me rends compte que je suis en retard pour le parti, mais dans le scénario (différent de l'exemple du PO) où une exception est effectivement levée. États MSDN ( https://msdn.Microsoft.com/en-us/library/zwc8s4fz.aspx ): "Si l'exception n'est pas interceptée, l'exécution du bloc finally dépend de la décision du système d'exploitation de déclencher ou non une opération de déroulement de l'exception." 

Le bloc finally n'est que garanti à exécuter si une autre fonction (telle que Main) située plus haut dans la pile d'appels intercepte l'exception. Ce détail ne pose généralement pas de problème, car tous les programmes C # d’environnement d’exécution (R & S) s’exécutent sur la plupart des ressources disponibles d’un processus lorsqu’il se termine (descripteurs de fichier, etc.). Dans certains cas, il peut toutefois s'avérer crucial: Une opération de base de données à moitié en cours que vous souhaitez commettre, puis resp. se détendre; ou une connexion distante qui ne peut pas être fermée automatiquement par le système d'exploitation et bloque ensuite un serveur. 

6
Peter A. Schneider

Oui. C’est en fait l’essentiel d’une déclaration finale. À moins d'un événement catastrophique (manque de mémoire, ordinateur débranché, etc.), l'instruction finally doit toujours être exécutée.

3

Il ne sera également pas déclenché sur une exception non capturée et s’exécutant dans un thread hébergé dans un service Windows

Enfin, n'est pas exécuté lorsqu'un thread est exécuté dans un service Windows

2
Stig

enfin, ne fonctionnera pas si vous quittez l'application avec System.exit (0); un péché

try
{
    System.out.println("try");
    System.exit(0);
}
finally
{
   System.out.println("finally");
}

le résultat serait juste: essayer

2
Hakuna Matata

Oui enfin appeler.

  public static bool someMethod()
    {
        try
        {

            return true;
            throw new Exception("test"); // doesn't seem to get executed
        }
        finally
        {
            //code in question
        }
    }
0
Sunil Dhappadhule

99% des scénarios garantissent que le code du bloc finally sera exécuté, mais pensez au scénario suivant: vous avez un thread qui a un bloc try-> finally (pas de catch) et une exception non gérée à l'intérieur de celui-ci. fil. Dans ce cas, le thread se fermera et son bloc finally ne sera pas exécuté (l'application peut continuer à s'exécuter dans ce cas).

Ce scénario est assez rare, mais c'est seulement pour montrer que la réponse n'est pas TOUJOURS "Oui", mais la plupart du temps "Oui" et parfois, dans de rares cas, "Non".

0
Jonathan Perry

Enfin, le but final de block est d'exécuter tout ce qui est écrit à l'intérieur. Cela ne devrait pas dépendre de ce qui se passe dans try ou catch.Cependant avec System.Environment.Exit (1), l'application se fermera sans passer à la ligne de code suivante.

0
Ashish Sahu