惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
DataBreaches.Net
N
Netflix TechBlog - Medium
F
Fortinet All Blogs
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
宝玉的分享
宝玉的分享
Y
Y Combinator Blog
博客园 - 聂微东
WordPress大学
WordPress大学
酷 壳 – CoolShell
酷 壳 – CoolShell
B
Blog RSS Feed
小众软件
小众软件
The GitHub Blog
The GitHub Blog
S
SegmentFault 最新的问题
Hugging Face - Blog
Hugging Face - Blog
Jina AI
Jina AI
Microsoft Azure Blog
Microsoft Azure Blog
V
V2EX
B
Blog
H
Help Net Security
D
Docker
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
罗磊的独立博客
月光博客
月光博客
博客园 - 司徒正美

博客园 - 毛毛虫

[Maui] 轮子——插件式开发 [Maui] Windows部署 [Maui] 造轮子——LoggerSqlite [Maui] 造轮子——MessageBox [Maui] 造轮子——前言、本地化 [C#] 日积月累 [.net10] 极简数据库对象关系映射 [C#] 扩展Enumerable,实现类似Sqlserver的窗口函数lag [Blazor] 学习随笔——呈现约定 [Blazor] 学习随笔——生命周期 [Blazor] 学习随笔——RZ10012警告的处理 [Blazor WebAssembly] 学习随笔——组件1.微信弹框(WXDialog) [Blazor WebAssembly] 学习随笔——身份验证 Asp.net Help ASP.NET Web 应用 Docker踩坑历程——续 [Net6]自己写个消息推送 Docker批处理笔记 Asp.net6.0 Swagger使用备忘 ASP.NET Web 应用 Docker踩坑历程
[Maui] 造轮子——Exception处理
毛毛虫 · 2026-03-09 · via 博客园 - 毛毛虫

上文MB有了显示异常的方法,这篇写异常处理。

一、全局未处理异常的处理

        AppDomain.CurrentDomain.UnhandledException += async (s, e) =>
        {
            if (e.ExceptionObject is Exception ex)
            {
                var source = "全局未处理异常 AppDomain";
                logger!.LogError(ex, source);
                await MB.ExceptionAsync(ex);

            }
        };
        TaskScheduler.UnobservedTaskException += async (s, e) =>
        {
            var source = "全局未处理异常 TaskScheduler";
            logger!.LogError(e.Exception, source);
            await MB.ExceptionAsync(e.Exception);
        };
#if ANDROID
        Android.Runtime.AndroidEnvironment.UnhandledExceptionRaiser += async (s, e) =>
        {
            e.Handled = true;
            var source = "全局未处理异常 Android.Runtime";
            logger!.LogError(e.Exception, source);
            await MB.ExceptionAsync(e.Exception);
        };
#else
        Microsoft.UI.Xaml.Application.Current.UnhandledException += async (s, e) =>
        {
            e.Handled = true;
            var source = "全局未处理异常UI.Xaml";
            logger!.LogError(e.Exception, source);
            await MB.ExceptionAsync(e.Exception);
        };
#endif

二、Fire-and-Forget 安全异步操作

  • 有些异常不是咱能控制的,只能询问是否需要重试?
  • 有些异常就是咱的锅,重试也没用,只能告知发生了什么,然后记入日志。
public static class FireService
{

    private static ILogger _Logger = default!;
    public static void Initialize(ILogger logger)
    {
        _Logger = logger;
    }
    #region 核心方法


    /// <summary>
    /// 执行异步操作,使用完整的错误处理
    /// </summary>
    public static async void FireAndForgetSafe(Func<Task> operation, Action<bool>? onFinish = null)
    {
        if (operation == null) return;

        while (true)
        {

            try
            {
                await operation().ConfigureAwait(false);
                onFinish?.Invoke(true);
            }
            catch (TaskCanceledException)
            {
                onFinish?.Invoke(false);
                return;
            }
            catch (OperationCanceledException)
            {
                onFinish?.Invoke(false);
                return;
            }
            catch (Exception ex)
            {
                // 处理异常
                if (await HandleAsync(ex))
                {
                    onFinish?.Invoke(false);
                    return;
                }
            }
        }
    }


    public static async Task<bool> FireSafeAsync(Func<Task> operation)
    {
        while (true)
        {
            try
            {
                await operation().ConfigureAwait(false);
                return true;
            }
            catch (TaskCanceledException)
            {
                return false;
            }
            catch (OperationCanceledException)
            {
                return false;
            }
            catch (Exception ex)
            {
                // 处理异常
                if (await HandleAsync(ex))
                    return false;
            }
        }
    }


    #endregion


    #region 内部方法
    private static async Task<bool> HandleAsync(Exception exception)
    {
        // 检查是否可重试
        if (!CanRetry(exception))
        {
            _Logger.LogError(exception, "FireService");
            await MB.ExceptionAsync(exception);
            return true;
        }
        //可重试
        var message = exception.GetFriendMessage();
        if (await MB.ConfirmAsync($"""
            {message}


            {Strings.Resource.Retry}?
            """))
            return false;
        else
        {
            _Logger.LogError(exception, "");
            return true;
        }
    }

    private static bool CanRetry(Exception ex)
    {
        // 检查异常类型
        return ex switch
        {
            HttpRequestException httpEx => CanRetryHttpException(httpEx),
            TimeoutException => true,
            IOException ioEx when IsNetworkRelated(ioEx) => true,
            _ => false
        };
    }

    private static bool CanRetryHttpException(HttpRequestException ex)
    {
        if (!ex.StatusCode.HasValue)
            return true; // 无状态码的网络错误,应该重试

        var statusCode = (int)ex.StatusCode.Value;

        // 5xx 服务器错误应该重试
        if (statusCode >= 500 && statusCode < 600)
            return true;

        // 429 请求过多应该重试
        if (statusCode == 429)
            return true;

        // 408 请求超时应该重试
        if (statusCode == 408)
            return true;

        return false;
    }

    private static bool IsNetworkRelated(IOException ex)
    {
        var message = ex.Message.ToLower();
        return message.Contains("network") ||
               message.Contains("connection") ||
               message.Contains("timeout");
    }


    #endregion
}

三、调用

  await FireService.FireSafeAsync(() =>
  {
      TestException();
      return Task.CompletedTask;

  });


...

  private static void TestException()
  {
      throw new TimeoutException();
  }

如此,其他地方再也用不着写try...catch,不用想着重试、不用想这次的异常该怎么提醒。简直就是难言之隐一洗了之。

当然,还是推送到了gitee

这是字数估计仍是不够。