MAUI程序挂在App.g.i.cs中 global::System.Diagnostics.Debugger.Break();的原因总结

最近在搞MAUI的时候,遇到了几次程序挂在App.g.i.cs中的这里:

UnhandledException += (sender, e) =>
{
    if (global::System.Diagnostics.Debugger.IsAttached) 
    	global::System.Diagnostics.Debugger.Break();
};

这个App.g.i.cs是MAUI自动生成的文件,而且此时的堆栈如下:
堆栈
可以看到都是系统底层的调用,所以不怎么能try-catch这个异常。

我用一个简单的项目复现了报这个异常的两种方式:

1. 在页面加载完成前导航走

首先创建两个Page,如MainPage.xaml和NewPage1.xaml。并且将它们添加到AppShell.xaml中:

<?xml version="1.0" encoding="UTF-8" ?>
<Shell
    x:Class="MAUIAppForTest.AppShell"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:MAUIAppForTest"
    Shell.FlyoutBehavior="Flyout">
    <FlyoutItem Title="New" 
                Route="NewPage1">
        <ShellContent
            Title="NewPage1"
            ContentTemplate="{DataTemplate local:NewPage1}"/>
    </FlyoutItem>
    
    <FlyoutItem x:Name="MainPageItem" Title="Repos" 
                Route="MainPage">
        <ShellContent
            Title="Github"
            ContentTemplate="{DataTemplate local:MainPage}"/>
    </FlyoutItem>
</Shell>

这样程序打开时会默认导航到NewPage1中。随后在NewPage1中如下写:

namespace MAUIAppForTest;

public partial class NewPage1 : ContentPage
{
	public NewPage1()
	{
		InitializeComponent();
	}

    protected override async void OnNavigatedTo(NavigatedToEventArgs args)
    {
        base.OnNavigatedTo(args);

        //await Task.Delay(500);
        await Shell.Current.GoToAsync("//MainPage");
    }
}

即当导航到NewPage1后,立刻导航到MainPage。如此运行程序,就会触发最开始提到的异常。

关于这个问题,我猜测是页面没加载完就被导航走了,通过这个[帖子]大家可以了解更多详细信息,里面也有人提到了通过Task.Delay的方式,延迟一会切换页面就可以。虽然我这里写的是Task.Delay(500)但实际上经过我的测试Task.Delay(1)就可以避免这个异常。

补充一个[Discussion],也是讨论这个问题的。

2. 用AppShell管理页面的时候使用DI的方式不对

这一点可能是个bug也可能是我用得不对,具体来说,我写了一个AppSettings类,想要通过依赖注入的方式,使得MainPage可以在构造函数中获得一个AppSettings的实例,具体的代码如下:

// MauiProgram.cs:
using MAUIAppForTest.Models;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace MAUIAppForTest;

public static class MauiProgram
{
	public static MauiApp CreateMauiApp()
	{
		var builder = MauiApp.CreateBuilder();
		builder
			.UseMauiApp<App>()
			.ConfigureFonts(fonts =>
			{
				fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
				fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
			});

		builder.Services.AddSingleton<AppSettings>();
		// builder.Services.AddTransient<MainPage>();
#if DEBUG
		builder.Logging.AddDebug();
#endif

		return builder.Build();
	}
}

// MainPage.xaml.cs:
using MAUIAppForTest.Models;

namespace MAUIAppForTest;

public partial class MainPage : ContentPage
{
	private AppSettings _appSettings;

	public MainPage(AppSettings appSettings)
    {
		InitializeComponent();
        _appSettings = appSettings;
		_appSettings.Print("MainPage");
    }
}

同时将AppShell.xaml中的MainPage移动到NewPage1前面,这样程序启动时默认打开的是MainPage,并在需要构造MainPage时,抛出前面提到的异常。

如果将AppShell.xaml中的NewPage1提到MainPage前面,也即是与第一个例子的逻辑相同,通过NewPage1导航到MainPage中,当然,这显然会报错的:

System.MissingMethodException:
“Cannot dynamically create an instance of type 'MAUIAppForTest.MainPage'. 
Reason: No parameterless constructor defined.

不过这个错很容易理解,就是因为GoToAsync("//MainPage")调用的是无参的构造函数,而现在MainPage没有无参的构造函数。不过这里也能够猜测到大概是MAUI的DI并没有起到作用,去调用无参的构造函数了,所以报错了。

如果这时候给MainPage加一个无参的构造函数,在其中调用Debug.Write("MainPage");的话,程序是可以正常运行并输出Debug的。并且如果我不是在MainPage的构造函数中添加AppSettings参数,而是在App的构造函数中添加AppSettings,那程序也能正常运行,其中App.xaml.cs的代码和运行效果如下:

using MAUIAppForTest.Models;

namespace MAUIAppForTest;

public partial class App : Application
{
	public App()
	{
		InitializeComponent();

		MainPage = new AppShell();
	}

	public App(AppSettings settings)
	{
		settings.Print("App From DI");
        InitializeComponent();
        MainPage = new AppShell();
	}
}

运行效果
那如何解决这个问题?我从这个[帖子]找到了解决方案,就是将Page也添加到builder.Services中,即在MauiProgram.cs中添加:builder.Services.AddTransient<MainPage>();

在上面那个帖子中,那个人遇到的错误是No parameterless constructor defined,然后有人发了几个issue和pr的链接,我看现在已经closed和merged了,那个bug已经修复了,不过好像使用AppShell管理页面的时候,给页面使用DI仍是有些问题的。

DI(Dependence Injection) 依赖注入
详细可参考:[Dependency injection]

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

F_CIL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值