2015-10-13 3 views
0

Удивительно исключение переполнения стека может быть вызвано многократным вызовом Window.ShowDialog асинхронно.C# WPF Window.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(); 
} 

Как вы можете видеть, что нет никакой фактической рекурсии здесь (или не должен быть), но как только происходит исключение вы можете увидеть, что стек вызовов действительно полно. Почему? Это также может быть достигнуто без использования таймера следующим образом:

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

P.S. Код, который вы видите здесь, сконструирован специально, чтобы проиллюстрировать вопрос, поэтому не сосредотачивайтесь на том, почему кто-либо сделает это. Цель вопроса - понять, почему ShowDialog ведет себя таким образом.

+0

Глядя на стек, следует прояснить проблему ... Если она не предоставит вам достаточной информации - отправьте небольшой повторный раздел стека на вопрос. –

ответ

4

Как вы можете видеть трассировку стека, каждый вызов в ShowDialog() подталкивает новые кадры к стеку. Поскольку вы вызываете ShowDialog() несколько раз без закрытия, каждый вызов увеличивает глубину стека, и стек в конечном итоге переполняется.

Это происходит потому, что в отличие от метода Show(), ShowDialog() не возвращается, пока отображаемое окно не будет закрыто. Это работает как любой другой вызов метода, поэтому он вызывает рост стека. Поскольку ShowDialog() должен отвечать на ввод пользователя, он запускает новый цикл диспетчера. Поскольку Диспетчер все еще работает, ваш таймер продолжает стрелять и открывать новые, вложенные диалоги.

Таким образом, на очень высоком уровне, ваш стек вызовов будет выглядеть следующим образом:

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

который в конечном итоге переполнения, как открываются новые диалоги.

+0

Ответ на вопрос «Это происходит потому, что в отличие от метода Show() ShowDialog() не возвращается до тех пор, пока не откроется окно, которое оно отображает. Это работает как любой другой вызов метода, поэтому он вызывает рост стека». Я не согласен. Если я заменю вызов ShowDialog с чем-то другим, который блокирует (не позволяет TheCall возвращаться), стек не накапливается (как и ожидалось). Таким образом, эти два предложения, по крайней мере, вводят в заблуждение. Но новый цикл диспетчера имеет смысл, поэтому спасибо за ответ. +1 –

Смежные вопросы