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

StackOverflow https://stackoverflow.com/questions/345091

Question

Je suis en train de réviser du code pour un ami et je lui 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 fonctionne pas?

Exemple:

public bool someMethod()
{
  try
  {
    return true;
    throw new Exception("test"); // doesn't seem to get executed
  }
  finally
  {
    //code in question
  }
}
Était-ce utile?

La solution

Réponse simple: oui.

Autres conseils

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 page régions d'exécution contraintes .

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

Citant depuis MSDN

  

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

Généralement, oui, le programme fonctionnera enfin.

Pour les trois scénarios suivants, TOUJOURS s'exécutera enfin:

  1. Aucune exception ne se produit
  2. Exceptions synchrones (exceptions qui se produisent dans le flux de programmes 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. Exception asynchrone ThreadAbortException
    À partir de .NET 2.0, une exception ThreadAbortException n'empêche plus une exécution définitive. 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, finalement ne sera pas exécuté:

Exception asynchrone StackOverflowException.
A partir de .NET 2.0, un débordement de pile entraînera l'arrêt du processus. Enfin, l'exécution ne sera pas exécutée, sauf si une contrainte supplémentaire est appliquée pour lui permettre finalement d'être 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 a été arrêté en cas de débordement de la pile et que tous les objets gérés seront 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 utilisé 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ées par le code utilisateur.

Par conséquent, le fait que le service final ne s'exécute pas sur StackOverflowException ne devrait pas avoir d'effet sur le code utilisateur, car le processus se terminera malgré tout. Si vous avez des cas particuliers dans lesquels vous devez nettoyer certaines ressources non gérées, 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 au (x) SafeHandle (s) approprié (s).

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.
}

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 déclenchez ou déclenchez une exception de toute sorte dans votre bloc catch (pas seulement < StackOverflowExceptions et autres choses étranges), et vous n'avez pas l'intégralité du bloc try / catch / finally dans un autre bloc try / catch , votre bloc finally ne sera pas exécuté. Ceci est facilement démontré - et si je ne l'avais pas vu moi-même, compte tenu de la fréquence à laquelle j'ai lu que ce n'est que vraiment bizarre, de minuscules cas de figure pouvant empêcher un bloc finally de s'exécuter, je ne l'aurait 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(<*>quot;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. (Il est noté ici par exemple, mais pas n'importe où dans cette question particulière.)

Je réalise que je suis en retard par rapport à la fête 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 du choix du système d’exploitation de déclencher une opération de déroulement par exception. "

Le bloc finally est seulement 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 être crucial: Une opération de base de données à moitié en cours que vous souhaitez commettre, puis. 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.

Oui. C’est en fait l’essentiel d’une déclaration finale. À moins que quelque chose de catestrophe ne se produise (mémoire insuffisante, ordinateur débranché, etc.), l'instruction finally doit toujours être exécutée.

Il ne sera pas non plus déclenché sur une exception non interceptée et exécuté dans un thread hébergé dans un service Windows

.

Enfin, n'est pas exécuté lors d'une Sujet exécuté dans un service Windows

enfin ne fonctionnera pas si vous quittez l'application avec System.exit (0); comme dans

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

le résultat serait juste: essayer

99% des scénarios garantissent que le code à l'intérieur du bloc finally sera exécuté, mais pensez à ce scénario: vous avez un fil qui a un try - > finally (aucun catch ) et vous obtenez une exception non gérée dans ce 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", c’est la plupart du temps "Oui". et parfois, dans de rares conditions, "Non".

Le but du blocage de finally 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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top