2015-10-13 1 views
0

De façon surprenante, une exception de dépassement de pile peut être provoquée en appelant de manière répétée Window.ShowDialog de manière asynchrone.C# WPF Exception de dépassement de pile Windows.ShowDialog

public MainWindow() 
{ 
    InitializeComponent(); 
    TheCallDelegate = TheCall; 
    _timer = new DispatcherTimer(); 
    _timer.Tick += _timer_Tick; 
    _timer.Start(); 
} 

DispatcherTimer _timer = null; 

void _timer_Tick(object sender, EventArgs e) 
{ 
    _timer.Dispatcher.BeginInvoke(TheCallDelegate);    
} 

Action TheCallDelegate; 

void TheCall() 
{ 
    Window win = new Window(); 
    win.ShowDialog(); 
} 

Comme vous pouvez le voir, il n'y a pas récursion réelle ici (ou il ne devrait pas avoir été) mais une fois que l'exception arrive, vous pouvez voir que la pile d'appel est en effet complet. Pourquoi? Cela peut également être obtenue sans l'utilisation d'une minuterie comme ceci:

private async void Button_Click(object sender, RoutedEventArgs e) 
    { 
     while (true) 
     { 
      this.Dispatcher.BeginInvoke(TheCallDelegate); 
      await Task.Delay(1); 
     } 
    } 

post-scriptum Le code que vous voyez ici est construit spécifiquement pour illustrer la question, donc ne vous concentrez pas sur la raison pour laquelle quelqu'un le ferait. Le but de la question est de comprendre pourquoi ShowDialog se comporte de cette manière.

+0

En regardant la pile devrait clarifier le problème ... Si elle ne fournit pas suffisamment d'informations à vous - poster une petite section répétée de la pile à la question. –

Répondre

4

Comme vous devriez être en mesure de voir la trace de la pile chaque appel à ShowDialog() pousse de nouvelles images sur la pile. Puisque vous appelez ShowDialog() plusieurs fois sans fermer, chaque appel augmente la profondeur de la pile, et la pile finit par déborder. Cela se produit car, contrairement à la méthode Show(), ShowDialog() ne retourne pas tant que la fenêtre affichée n'est pas fermée. Cela fonctionne comme n'importe quel autre appel de méthode, donc cela fait grandir la pile. Puisque ShowDialog() doit répondre à l'entrée de l'utilisateur, il démarre une nouvelle boucle Dispatcher. Comme un Dispatcher est toujours en cours d'exécution, votre minuteur continue de déclencher et d'ouvrir de nouveaux dialogues imbriqués.

donc à un très haut niveau de votre pile d'appel ressemblera:

...Dispatcher Loop... 
TheCall 
ShowDialog 
... Dialog Dispatcher Loop... 
TheCall 
ShowDialog 
... Dialog Dispatcher Loop... 
TheCall 
ShowDialog 
... Dialog Dispatcher Loop... 

qui finira par déborder de nouvelles boîtes de dialogue sont ouvertes.

+0

En réponse à "Cela se produit parce que contrairement à la méthode Show(), ShowDialog() ne retourne pas tant que la fenêtre affichée n'est pas fermée, cela fonctionne comme n'importe quel autre appel de méthode, ce qui augmente la pile." Je ne suis pas d'accord. Si je remplace un appel à ShowDialog par quelque chose d'autre qui bloque (ne laisse pas retourner TheCall) la pile ne s'accumule pas (comme prévu). Donc ces deux phrases au moins sont trompeuses. Mais la nouvelle boucle Dispatcher a du sens, merci pour la réponse. +1 –