簡體   English   中英

C#WPF Window.ShowDialog堆棧溢出異常

[英]C# WPF Window.ShowDialog stack overflow exception

令人驚訝的是,通過重復異步調用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);
        }
    }

PS:您在此處看到的代碼是專門為說明問題而構造的,因此不要着重於為什么有人會這樣做。 該問題的目的是了解為什么ShowDialog會以這種方式運行。

正如您應該能夠看到堆棧跟蹤一樣,每次對ShowDialog()調用都會將新幀推送到堆棧上。 由於您多次調用ShowDialog()而不關閉,因此每次調用都會增加堆棧深度,並且堆棧最終會溢出。

發生這種情況是因為與Show()方法不同, ShowDialog()直到關閉顯示的窗口才返回。 這與任何其他方法調用一樣工作,因此會導致堆棧增長。 由於ShowDialog()必須響應用戶輸入,因此它將啟動新的Dispatcher循環。 由於分派器仍在運行,因此計時器會不斷觸發並打開新的嵌套對話框。

因此,在很高的層次上,您的調用堆棧將如下所示:

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

隨着新對話框的打開,它將最終溢出。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM