C#WPF Window.ShowDialog堆栈溢出异常
令人惊讶的是,反复异步调用Window.ShowDialog可能会导致堆栈溢出异常C#WPF Window.ShowDialog堆栈溢出异常,c#,wpf,stack-overflow,showdialog,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
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);
}
}
另外,你在这里看到的代码是专门用来说明这个问题的,所以不要关注为什么会有人这样做。该问题的目的是了解ShowDialog为何会以这种方式运行。因为您应该能够看到堆栈跟踪,每次调用
ShowDialog()
都会将新帧推送到堆栈上。由于您多次调用ShowDialog()
而没有关闭,因此每次调用都会增加堆栈深度,堆栈最终会溢出
这是因为与Show()
方法不同,ShowDialog()
在显示的窗口关闭之前不会返回。这与任何其他方法调用一样,因此会导致堆栈增长。由于ShowDialog()
必须响应用户输入,因此它会启动一个新的调度程序循环。由于调度器仍在运行,所以计时器会不断触发并打开新的嵌套对话框
因此,在非常高的级别上,您的调用堆栈将如下所示:
...Dispatcher Loop...
TheCall
ShowDialog
... Dialog Dispatcher Loop...
TheCall
ShowDialog
... Dialog Dispatcher Loop...
TheCall
ShowDialog
... Dialog Dispatcher Loop...
当新对话框打开时,最终会溢出。因为您应该能够看到堆栈跟踪,所以对
ShowDialog()
的每次调用都会将新帧推送到堆栈上。由于您多次调用ShowDialog()
而没有关闭,因此每次调用都会增加堆栈深度,堆栈最终会溢出
这是因为与Show()
方法不同,ShowDialog()
在显示的窗口关闭之前不会返回。这与任何其他方法调用一样,因此会导致堆栈增长。由于ShowDialog()
必须响应用户输入,因此它会启动一个新的调度程序循环。由于调度器仍在运行,所以计时器会不断触发并打开新的嵌套对话框
因此,在非常高的级别上,您的调用堆栈将如下所示:
...Dispatcher Loop...
TheCall
ShowDialog
... Dialog Dispatcher Loop...
TheCall
ShowDialog
... Dialog Dispatcher Loop...
TheCall
ShowDialog
... Dialog Dispatcher Loop...
随着新对话框的打开,最终会溢出。查看堆栈应该可以澄清问题。。。如果它没有提供足够的信息给你-张贴堆栈的小重复部分的问题。查看堆栈应该澄清问题。。。如果它没有向您提供足够的信息-将堆栈的一小部分重复发布到问题中。回答“这是因为与Show()方法不同,ShowDialog()在其显示的窗口关闭之前不会返回。这与任何其他方法调用一样,因此会导致堆栈增长。”我不同意。如果我将对ShowDialog的调用替换为阻塞(不让调用返回)的其他调用,堆栈不会堆积(如预期的那样)。所以这两句话至少有误导性。但是新的Dispatcher循环是有意义的,所以感谢您的回答+1响应“这是因为与Show()方法不同,ShowDialog()在显示的窗口关闭之前不会返回。这与任何其他方法调用一样,因此会导致堆栈增长。”我不同意。如果我将对ShowDialog的调用替换为阻塞(不让调用返回)的其他调用,堆栈不会堆积(如预期的那样)。所以这两句话至少有误导性。但是新的Dispatcher循环是有意义的,所以感谢您的回答+1.