










上文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
这是字数估计仍是不够。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。