From 7c3cda9682f43be4ab8e62f7f9b4edbf74585860 Mon Sep 17 00:00:00 2001 From: Soar360 Date: Fri, 26 Jun 2026 15:24:05 +0800 Subject: [PATCH 1/4] feat(common): migrate Guard to LuYao.Common with tests and docs --- src/LuYao.Common/Guard.cs | 125 +++++++++++++++++++++ tests/LuYao.Common.UnitTests/GuardTests.cs | 95 ++++++++++++++++ 2 files changed, 220 insertions(+) create mode 100644 src/LuYao.Common/Guard.cs create mode 100644 tests/LuYao.Common.UnitTests/GuardTests.cs diff --git a/src/LuYao.Common/Guard.cs b/src/LuYao.Common/Guard.cs new file mode 100644 index 0000000..910cc73 --- /dev/null +++ b/src/LuYao.Common/Guard.cs @@ -0,0 +1,125 @@ +using System; +using System.Threading.Tasks; + +namespace LuYao; + +/// +/// Provides guarded execution helpers to reduce try/catch boilerplate +/// while keeping behavior observable and configurable. +/// +public static class Guard +{ + /// + /// Executes an action with guarded exception handling. + /// + /// The action to execute. + /// An optional callback invoked when a handled exception is caught. + /// An optional predicate to determine whether an exception should be handled. + /// If , rethrows handled exceptions after invoking . + /// when execution succeeds; otherwise for handled exceptions. + public static bool TryRun(Action action, Action? onError = null, Func? when = null, bool rethrow = false) + { + try + { + action(); + return true; + } + catch (Exception ex) when (CanHandle(ex, when)) + { + onError?.Invoke(ex); + if (rethrow) throw; + return false; + } + } + + /// + /// Executes an asynchronous action with guarded exception handling. + /// + /// The asynchronous action to execute. + /// An optional callback invoked when a handled exception is caught. + /// An optional predicate to determine whether an exception should be handled. + /// If , rethrows handled exceptions after invoking . + /// A task that returns when execution succeeds; otherwise for handled exceptions. + public static async Task TryRunAsync(Func action, Action? onError = null, Func? when = null, bool rethrow = false) + { + try + { + await action(); + return true; + } + catch (Exception ex) when (CanHandle(ex, when)) + { + onError?.Invoke(ex); + if (rethrow) throw; + return false; + } + } + + /// + /// Executes a function and returns its result, or a fallback value when a handled exception occurs. + /// + /// The result type. + /// The function to execute. + /// The fallback value returned when a handled exception occurs. + /// An optional callback invoked when a handled exception is caught. + /// An optional predicate to determine whether an exception should be handled. + /// If , rethrows handled exceptions after invoking . + /// The function result when successful; otherwise for handled exceptions. + public static T TryGet(Func func, T fallback = default!, Action? onError = null, Func? when = null, bool rethrow = false) + { + try + { + return func(); + } + catch (Exception ex) when (CanHandle(ex, when)) + { + onError?.Invoke(ex); + if (rethrow) throw; + return fallback; + } + } + + /// + /// Executes an asynchronous function and returns its result, + /// or a fallback value when a handled exception occurs. + /// + /// The result type. + /// The asynchronous function to execute. + /// The fallback value returned when a handled exception occurs. + /// An optional callback invoked when a handled exception is caught. + /// An optional predicate to determine whether an exception should be handled. + /// If , rethrows handled exceptions after invoking . + /// A task that returns the function result when successful; otherwise for handled exceptions. + public static async Task TryGetAsync(Func> func, T fallback = default!, Action? onError = null, Func? when = null, bool rethrow = false) + { + try + { + return await func(); + } + catch (Exception ex) when (CanHandle(ex, when)) + { + onError?.Invoke(ex); + if (rethrow) throw; + return fallback; + } + } + + private static bool CanHandle(Exception ex, Func? when) + { + if (IsCritical(ex)) return false; + if (when == null) return true; + return when(ex); + } + + private static bool IsCritical(Exception ex) + { + return ex is OperationCanceledException + || ex is OutOfMemoryException + || ex is StackOverflowException + || ex is AccessViolationException + || ex is AppDomainUnloadedException + || ex is BadImageFormatException + || ex is CannotUnloadAppDomainException + || ex is InvalidProgramException; + } +} \ No newline at end of file diff --git a/tests/LuYao.Common.UnitTests/GuardTests.cs b/tests/LuYao.Common.UnitTests/GuardTests.cs new file mode 100644 index 0000000..18cf131 --- /dev/null +++ b/tests/LuYao.Common.UnitTests/GuardTests.cs @@ -0,0 +1,95 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace LuYao; + +[TestClass] +public class GuardTests +{ + [TestMethod] + public void TryRun_WhenActionSucceeds_ReturnsTrue() + { + bool result = Guard.TryRun(() => { }); + + Assert.IsTrue(result); + } + + [TestMethod] + public void TryRun_WhenActionThrows_ReturnsFalseAndInvokesOnError() + { + Exception? captured = null; + + bool result = Guard.TryRun(() => throw new InvalidOperationException("boom"), ex => captured = ex); + + Assert.IsFalse(result); + Assert.IsNotNull(captured); + Assert.IsInstanceOfType(captured); + } + + [TestMethod] + public void TryRun_WhenRethrowIsTrue_RethrowsHandledException() + { + Assert.Throws(() => + Guard.TryRun(() => throw new InvalidOperationException("boom"), rethrow: true)); + } + + [TestMethod] + public void TryRun_WhenFilterReturnsFalse_DoesNotHandleException() + { + Assert.Throws(() => + Guard.TryRun(() => throw new InvalidOperationException("boom"), when: _ => false)); + } + + [TestMethod] + public void TryRun_WhenOperationCanceledExceptionThrown_DoesNotHandleException() + { + Assert.Throws(() => + Guard.TryRun(() => throw new OperationCanceledException())); + } + + [TestMethod] + public void TryGet_WhenFuncThrows_ReturnsFallback() + { + int value = Guard.TryGet(() => throw new InvalidOperationException("boom"), fallback: 42); + + Assert.AreEqual(42, value); + } + + [TestMethod] + public async Task TryRunAsync_WhenActionThrows_ReturnsFalseAndInvokesOnError() + { + Exception? captured = null; + + bool result = await Guard.TryRunAsync( + async () => + { + await Task.Yield(); + throw new InvalidOperationException("boom"); + }, + ex => captured = ex); + + Assert.IsFalse(result); + Assert.IsNotNull(captured); + Assert.IsInstanceOfType(captured); + } + + [TestMethod] + public async Task TryRunAsync_WhenOperationCanceledExceptionThrown_DoesNotHandleException() + { + await Assert.ThrowsAsync(async () => + await Guard.TryRunAsync(async () => + { + await Task.Yield(); + throw new OperationCanceledException(); + })); + } + + [TestMethod] + public async Task TryGetAsync_WhenFuncThrows_ReturnsFallback() + { + int value = await Guard.TryGetAsync( + () => Task.FromException(new InvalidOperationException("boom")), + fallback: 64); + + Assert.AreEqual(64, value); + } +} \ No newline at end of file From 626395cc9bb9887c1211a63130e68814e0e33a0b Mon Sep 17 00:00:00 2001 From: Soar360 Date: Fri, 26 Jun 2026 20:15:15 +0800 Subject: [PATCH 2/4] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/LuYao.Common/Guard.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/LuYao.Common/Guard.cs b/src/LuYao.Common/Guard.cs index 910cc73..3c8440d 100644 --- a/src/LuYao.Common/Guard.cs +++ b/src/LuYao.Common/Guard.cs @@ -17,10 +17,11 @@ public static class Guard /// An optional predicate to determine whether an exception should be handled. /// If , rethrows handled exceptions after invoking . /// when execution succeeds; otherwise for handled exceptions. - public static bool TryRun(Action action, Action? onError = null, Func? when = null, bool rethrow = false) - { - try - { +public static bool TryRun(Action action, Action? onError = null, Func? when = null, bool rethrow = false) +{ + if (action == null) throw new ArgumentNullException(nameof(action)); + + try action(); return true; } From 9c0d59ee134ad7f74dc245b19db08b42c9ace708 Mon Sep 17 00:00:00 2001 From: Soar360 Date: Fri, 26 Jun 2026 20:16:04 +0800 Subject: [PATCH 3/4] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/LuYao.Common/Guard.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/LuYao.Common/Guard.cs b/src/LuYao.Common/Guard.cs index 3c8440d..f0a3320 100644 --- a/src/LuYao.Common/Guard.cs +++ b/src/LuYao.Common/Guard.cs @@ -66,10 +66,11 @@ public static async Task TryRunAsync(Func action, Action? /// An optional predicate to determine whether an exception should be handled. /// If , rethrows handled exceptions after invoking . /// The function result when successful; otherwise for handled exceptions. - public static T TryGet(Func func, T fallback = default!, Action? onError = null, Func? when = null, bool rethrow = false) - { - try - { +public static T TryGet(Func func, T fallback = default!, Action? onError = null, Func? when = null, bool rethrow = false) +{ + if (func == null) throw new ArgumentNullException(nameof(func)); + + try return func(); } catch (Exception ex) when (CanHandle(ex, when)) @@ -91,10 +92,11 @@ public static T TryGet(Func func, T fallback = default!, Action /// An optional predicate to determine whether an exception should be handled. /// If , rethrows handled exceptions after invoking . /// A task that returns the function result when successful; otherwise for handled exceptions. - public static async Task TryGetAsync(Func> func, T fallback = default!, Action? onError = null, Func? when = null, bool rethrow = false) - { - try - { +public static async Task TryGetAsync(Func> func, T fallback = default!, Action? onError = null, Func? when = null, bool rethrow = false) +{ + if (func == null) throw new ArgumentNullException(nameof(func)); + + try return await func(); } catch (Exception ex) when (CanHandle(ex, when)) From 0261aaad6646b25079deb3c99c628ea9bf02044b Mon Sep 17 00:00:00 2001 From: Soar360 Date: Fri, 26 Jun 2026 20:20:41 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E9=87=8D=E6=9E=84=20Guard=20=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E4=B8=BA=E5=A4=9A=E8=A1=8C=E5=A4=A7=E6=8B=AC=E5=8F=B7?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 Guard.cs 中 TryRun、TryGet、TryGetAsync 方法由单行实现重构为标准多行大括号格式,提升代码可读性和一致性,未更改方法逻辑。 --- src/LuYao.Common/Guard.cs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/LuYao.Common/Guard.cs b/src/LuYao.Common/Guard.cs index f0a3320..9d6e208 100644 --- a/src/LuYao.Common/Guard.cs +++ b/src/LuYao.Common/Guard.cs @@ -17,11 +17,12 @@ public static class Guard /// An optional predicate to determine whether an exception should be handled. /// If , rethrows handled exceptions after invoking . /// when execution succeeds; otherwise for handled exceptions. -public static bool TryRun(Action action, Action? onError = null, Func? when = null, bool rethrow = false) -{ - if (action == null) throw new ArgumentNullException(nameof(action)); + public static bool TryRun(Action action, Action? onError = null, Func? when = null, bool rethrow = false) + { + if (action == null) throw new ArgumentNullException(nameof(action)); - try + try + { action(); return true; } @@ -66,11 +67,12 @@ public static async Task TryRunAsync(Func action, Action? /// An optional predicate to determine whether an exception should be handled. /// If , rethrows handled exceptions after invoking . /// The function result when successful; otherwise for handled exceptions. -public static T TryGet(Func func, T fallback = default!, Action? onError = null, Func? when = null, bool rethrow = false) -{ - if (func == null) throw new ArgumentNullException(nameof(func)); + public static T TryGet(Func func, T fallback = default!, Action? onError = null, Func? when = null, bool rethrow = false) + { + if (func == null) throw new ArgumentNullException(nameof(func)); - try + try + { return func(); } catch (Exception ex) when (CanHandle(ex, when)) @@ -92,11 +94,12 @@ public static T TryGet(Func func, T fallback = default!, Action /// An optional predicate to determine whether an exception should be handled. /// If , rethrows handled exceptions after invoking . /// A task that returns the function result when successful; otherwise for handled exceptions. -public static async Task TryGetAsync(Func> func, T fallback = default!, Action? onError = null, Func? when = null, bool rethrow = false) -{ - if (func == null) throw new ArgumentNullException(nameof(func)); + public static async Task TryGetAsync(Func> func, T fallback = default!, Action? onError = null, Func? when = null, bool rethrow = false) + { + if (func == null) throw new ArgumentNullException(nameof(func)); - try + try + { return await func(); } catch (Exception ex) when (CanHandle(ex, when))