C#WPF Window.ShowDialog堆栈溢出异常

时间:2015-10-13 18:38:18

标签: c# wpf stack-overflow showdialog

令人惊讶的是,异步重复调用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以这种方式表现。

1 个答案:

答案 0 :(得分:4)

由于您应该能够看到堆栈跟踪,因此每次调用ShowDialog()都会将新帧推送到堆栈中。由于您多次调用ShowDialog()而没有关闭,因此每次调用都会增加堆栈深度,并且堆栈最终会溢出。

这是因为与Show()方法不同,ShowDialog()在它显示的窗口关闭之前不会返回。这与任何其他方法调用一样,因此它会导致堆栈增长。由于ShowDialog()必须响应用户输入,因此它会启动一个新的Dispatcher循环。由于Dispatcher仍在运行,因此计时器会持续触发并打开新的嵌套对话框。

因此,在非常高的级别,您的调用堆栈将如下所示:

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

随着新对话框的打开,最终会溢出。