web-dev-qa-db-fra.com

Le bloc C # "enfin" s'exécute-t-il TOUJOURS?

Duplicata possible:
Le code dans une instruction Final se déclenchera-t-il si je renvoie une valeur dans un bloc Try?

Considérez le code suivant code C #. Le bloc "enfin" s'exécute-t-il?

public void DoesThisExecute() {
   string ext = "xlsx";
   string message = string.Empty;
   try {
      switch (ext) {
         case "xls": message = "Great choice!"; break;
         case "csv": message = "Better choice!"; break;
         case "exe": message = "Do not try to break me!"; break;
         default:
            message = "You will not win!";
            return;
      }
   }
   catch (Exception) {
      // Handle an exception.
   }
   finally {
      MessageBox.Show(message);
   }
}

Ha, après avoir fini d'écrire ceci, j'ai réalisé que j'aurais pu le tester moi-même dans Visual Studio. N'hésitez pas à répondre!

65
Ryan Rodemoyer
5
Ian Jacobs

Non. Il s'exécutera toujours à condition que l'application soit toujours en cours d'exécution (sauf lors d'une exception FastFail , lien MSDN , comme d'autres l'ont noté). Il s'exécutera lorsqu'il quittera la partie try/catch du bloc.

Il ne s'exécutera PAS si l'application se bloque: se fait tuer par une commande de processus de suppression, etc. Ceci est très important, car si vous écrivez du code qui s'attend absolument à ce qu'il s'exécute, comme faire manuellement une restauration, et sinon, il le fera automatiquement commit, vous pouvez exécuter dans un scénario que l'application abandonne avant que cela ne se produise. Honnêtement, il s'agit d'un scénario extérieur, mais il est important d'en tenir compte dans ces situations.

80
kemiller2002

À partir de la spécification MSDN C # de l'instruction try:

Les instructions d'un bloc finally sont toujours exécutées lorsque le contrôle quitte une instruction try. Cela est vrai que le transfert de contrôle se produise à la suite d'une exécution normale, à la suite de l'exécution d'une instruction break, continue, goto ou return , ou suite à la propagation d'une exception à partir de l'instruction try.

Source

Il y a des cas où le bloc finally ne s'exécutera pas:

  1. Environment.FailFast
  2. Types d'exception inaccessibles
  3. Panne électrique
50
msarchet

Il n'est pas totalement vrai que finally sera toujours exécuté. Voir cette réponse de Haacked :

Deux possibilités:

  • StackOverflowException
  • ExecutingEngineException

Le bloc finally ne sera pas exécuté lorsqu'il y a une StackOverflowException car il n'y a pas de place sur la pile pour même exécuter plus de code. Elle ne sera pas non plus appelée lorsqu'il y a une ExecutingEngineException, ce qui est très rare.

En fait, pour toute sorte d'exception asynchrone (comme StackOverflowException, OutOfMemoryException, ThreadAbortException), l'exécution d'un bloc finally n'est pas garantie.

Cependant, ces exceptions sont des exceptions que vous ne pouvez généralement pas récupérer et, dans la plupart des cas, votre processus se termine de toute façon.

En fait, il y a aussi au moins un autre cas où finally n'est pas exécuté comme décrit par Brian Rasmussen dans un maintenant question supprimée :

L'autre cas que je connais est si un finaliseur lève une exception. Dans ce cas, le processus prend également fin immédiatement et la garantie ne s'applique donc pas.

Le code ci-dessous illustre le problème

static void Main(string[] args) {
   try {
      DisposableType d = new DisposableType();
      d.Dispose();
      d = null;
      GC.Collect();
      GC.WaitForPendingFinalizers();
   } catch {
      Console.WriteLine("catch");
   } finally {
      Console.WriteLine("finally");
   }
}

public class DisposableType : IDisposable {
   public void Dispose() {
   }

   ~DisposableType() {
      throw new NotImplementedException();
   }
}

Un essai fiable/catch/enfin devra utiliser régions d'exécution contraintes (CER) . Un exemple est fourni par MSDN:

[StructLayout(LayoutKind.Sequential)]
struct MyStruct
{
    public IntPtr m_outputHandle;
}

sealed class MySafeHandle : SafeHandle
{
    // Called by P/Invoke when returning SafeHandles
    public MySafeHandle()
        : base(IntPtr.Zero, true)
    {
    }

    public MySafeHandle AllocateHandle()
    {
        // Allocate SafeHandle first to avoid failure later.
        MySafeHandle sh = new MySafeHandle();

        RuntimeHelpers.PrepareConstrainedRegions();
        try { }
        finally
        {
            MyStruct myStruct = new MyStruct();
            NativeAllocateHandle(ref myStruct);
            sh.SetHandle(myStruct.m_outputHandle);
        }

        return sh;
    }
}

Une excellente source d'information est l'article suivant:

Meilleures pratiques de fiabilité

33
Dirk Vollmar

Depuis MSDN try-finally (référence C #)

Le bloc finally est utile pour nettoyer toutes les ressources allouées dans le bloc try ainsi que pour exécuter tout code qui doit s'exécuter même s'il existe une exception. Le contrôle est toujours passé au bloc finally quelle que soit la façon dont le bloc try se termine .

4
Leniel Maccaferri

Oui, dans des circonstances normales (comme beaucoup d'autres l'ont souligné).

Le bloc finally est utile pour nettoyer toutes les ressources allouées dans le bloc try ainsi que pour exécuter tout code qui doit s'exécuter même s'il existe une exception. Le contrôle est toujours transmis au bloc finally, quelle que soit la façon dont le bloc try se termine.

Alors que catch est utilisé pour gérer les exceptions qui se produisent dans un bloc d'instructions, finalement est utilisé pour garantir l'exécution d'un bloc d'instructions de code, quelle que soit la façon dont le bloc try précédent est quitté.

http://msdn.Microsoft.com/en-us/library/zwc8s4fz.aspx

2
Zano

Le bloc enfin s'exécutera, juste entre ces lignes:

message = "You will not win!";
return;
2
David Crowell

Oui, finally s'exécute toujours, maintenant si le code du bloc finally provoquera une exception est une autre histoire.

1
Quintin Robinson

La bonne réponse est oui.

Essayez de déboguer votre programme et de mettre un point d'arrêt et regardez comme le contrôle frappe toujours le bloc finalement.

1
JonH

Non, ce n'est pas le cas.

Il n'y a qu'une seule façon de contourner cela, c'est Environment.FailFast(). Voir http://msdn.Microsoft.com/de-de/library/ms131100.aspx . Dans tous les autres cas, il est garanti que les finaliseurs seront exécutés ;-)

La méthode FailFast écrit la chaîne de message dans le journal des événements de l'application Windows, crée un vidage de votre application, puis termine le processus en cours. La chaîne de message est également incluse dans le rapport d'erreurs à Microsoft.

Utilisez la méthode FailFast au lieu de la méthode Exit pour terminer votre application si l'état de votre application est endommagé de manière irréparable, et l'exécution des blocs et finaliseurs try/finally de votre application corrompra les ressources du programme.

1
Johannes Rudolph

Réponse simple Oui. Mais il y a quelques "exceptions" à la règle.

0
Jerod Houghtelling