diff --git a/GFramework.Core/functional/control/ControlExtensions.cs b/GFramework.Core/functional/control/ControlExtensions.cs index 90ebc9d..412c9fc 100644 --- a/GFramework.Core/functional/control/ControlExtensions.cs +++ b/GFramework.Core/functional/control/ControlExtensions.cs @@ -47,4 +47,179 @@ public static class ControlExtensions { return !predicate(value) ? value : null; } + + /// + /// TakeIfValue:值类型版本的 TakeIf,返回 Nullable + /// + /// 值类型 + /// 要进行条件判断的输入值 + /// 条件判断函数 + /// 条件为真时返回原值,否则返回 null + /// 当 predicate 为 null 时抛出 + /// + /// + /// var result = 42.TakeIfValue(x => x > 0); // 42 + /// var result2 = -5.TakeIfValue(x => x > 0); // null + /// + /// + public static TSource? TakeIfValue( + this TSource value, + Func predicate) + where TSource : struct + { + ArgumentNullException.ThrowIfNull(predicate); + return predicate(value) ? value : null; + } + + /// + /// TakeUnlessValue:值类型版本的 TakeUnless,返回 Nullable + /// + /// 值类型 + /// 要进行条件判断的输入值 + /// 条件判断函数 + /// 条件为假时返回原值,否则返回 null + /// 当 predicate 为 null 时抛出 + /// + /// x < 0); // 42 + /// var result2 = -5.TakeUnlessValue(x => x < 0); // null + /// ]]> + /// + public static TSource? TakeUnlessValue( + this TSource value, + Func predicate) + where TSource : struct + { + ArgumentNullException.ThrowIfNull(predicate); + return !predicate(value) ? value : null; + } + + /// + /// When:条件执行,满足条件时执行操作并返回原值 + /// + /// 值的类型 + /// 输入值 + /// 条件判断函数 + /// 满足条件时执行的操作 + /// 原始值 + /// 当 predicate 或 action 为 null 时抛出 + /// + /// + /// var result = 42 + /// .When(x => x > 0, x => Console.WriteLine($"Positive: {x}")) + /// .When(x => x % 2 == 0, x => Console.WriteLine("Even")); + /// + /// + public static TSource When( + this TSource value, + Func predicate, + Action action) + { + ArgumentNullException.ThrowIfNull(predicate); + ArgumentNullException.ThrowIfNull(action); + + if (predicate(value)) + action(value); + + return value; + } + + /// + /// RepeatUntil:重复执行函数直到条件满足 + /// + /// 值的类型 + /// 初始值 + /// 每次迭代执行的函数 + /// 停止条件 + /// 最大迭代次数(防止无限循环) + /// 满足条件时的值 + /// 当 func 或 predicate 为 null 时抛出 + /// 当 maxIterations 小于 1 时抛出 + /// 当达到最大迭代次数仍未满足条件时抛出 + /// + /// + /// var result = 1.RepeatUntil( + /// x => x * 2, + /// x => x >= 100, + /// maxIterations: 10 + /// ); // 128 + /// + /// + public static TSource RepeatUntil( + this TSource value, + Func func, + Func predicate, + int maxIterations = 1000) + { + ArgumentNullException.ThrowIfNull(func); + ArgumentNullException.ThrowIfNull(predicate); + ArgumentOutOfRangeException.ThrowIfLessThan(maxIterations, 1); + + var current = value; + var iterations = 0; + + while (!predicate(current)) + { + if (iterations >= maxIterations) + throw new InvalidOperationException( + $"RepeatUntil 达到最大迭代次数 {maxIterations} 但条件仍未满足"); + + current = func(current); + iterations++; + } + + return current; + } + + /// + /// Retry:同步重试机制,失败时重试指定次数 + /// + /// 返回值类型 + /// 要执行的函数 + /// 最大重试次数 + /// 每次重试之间的延迟(毫秒) + /// 函数执行结果 + /// 当 func 为 null 时抛出 + /// 当 maxRetries 小于 0 或 delayMilliseconds 小于 0 时抛出 + /// 当所有重试都失败时抛出,包含所有异常 + /// + /// + /// var result = ControlExtensions.Retry( + /// () => UnstableOperation(), + /// maxRetries: 3, + /// delayMilliseconds: 100 + /// ); + /// + /// + public static TResult Retry( + Func func, + int maxRetries = 3, + int delayMilliseconds = 0) + { + ArgumentNullException.ThrowIfNull(func); + ArgumentOutOfRangeException.ThrowIfNegative(maxRetries); + ArgumentOutOfRangeException.ThrowIfNegative(delayMilliseconds); + + var exceptions = new List(); + var attempts = maxRetries + 1; + + for (var i = 0; i < attempts; i++) + { + try + { + return func(); + } + catch (Exception ex) + { + exceptions.Add(ex); + + if (i < maxRetries && delayMilliseconds > 0) + Thread.Sleep(delayMilliseconds); + } + } + + throw new AggregateException( + $"操作在 {attempts} 次尝试后仍然失败", + exceptions); + } } \ No newline at end of file diff --git a/GFramework.Core/functional/functions/FunctionExtensions.cs b/GFramework.Core/functional/functions/FunctionExtensions.cs index ce1bd1f..8935946 100644 --- a/GFramework.Core/functional/functions/FunctionExtensions.cs +++ b/GFramework.Core/functional/functions/FunctionExtensions.cs @@ -99,4 +99,185 @@ public static class FunctionExtensions } #endregion + + #region Compose & AndThen + + /// + /// Compose:函数组合,返回 f(g(x)) + /// 数学表示:(f ∘ g)(x) = f(g(x)) + /// + /// 输入类型 + /// 中间类型 + /// 输出类型 + /// 外层函数 + /// 内层函数 + /// 组合后的函数 + /// 当 f 或 g 为 null 时抛出 + /// + /// + /// Func<int, int> addOne = x => x + 1; + /// Func<int, int> multiplyTwo = x => x * 2; + /// var composed = multiplyTwo.Compose(addOne); // (x + 1) * 2 + /// var result = composed(5); // (5 + 1) * 2 = 12 + /// + /// + public static Func Compose( + this Func f, + Func g) + { + ArgumentNullException.ThrowIfNull(f); + ArgumentNullException.ThrowIfNull(g); + return x => f(g(x)); + } + + /// + /// AndThen:函数链式调用,返回 g(f(x)) + /// 数学表示:(f >> g)(x) = g(f(x)) + /// + /// 输入类型 + /// 中间类型 + /// 输出类型 + /// 第一个函数 + /// 第二个函数 + /// 链式调用后的函数 + /// 当 f 或 g 为 null 时抛出 + /// + /// + /// Func<int, int> addOne = x => x + 1; + /// Func<int, int> multiplyTwo = x => x * 2; + /// var chained = addOne.AndThen(multiplyTwo); // (x + 1) * 2 + /// var result = chained(5); // (5 + 1) * 2 = 12 + /// + /// + public static Func AndThen( + this Func f, + Func g) + { + ArgumentNullException.ThrowIfNull(f); + ArgumentNullException.ThrowIfNull(g); + return x => g(f(x)); + } + + #endregion + + #region Curry & Uncurry + + /// + /// Curry:将二参数函数柯里化为嵌套的单参数函数 + /// + /// 第一个参数类型 + /// 第二个参数类型 + /// 返回值类型 + /// 要柯里化的函数 + /// 柯里化后的函数 + /// 当 func 为 null 时抛出 + /// + /// + /// Func<int, int, int> add = (x, y) => x + y; + /// var curriedAdd = add.Curry(); + /// var add5 = curriedAdd(5); + /// var result = add5(3); // 8 + /// + /// + public static Func> Curry( + this Func func) + { + ArgumentNullException.ThrowIfNull(func); + return arg1 => arg2 => func(arg1, arg2); + } + + /// + /// Curry:将三参数函数柯里化为嵌套的单参数函数 + /// + /// 第一个参数类型 + /// 第二个参数类型 + /// 第三个参数类型 + /// 返回值类型 + /// 要柯里化的函数 + /// 柯里化后的函数 + /// 当 func 为 null 时抛出 + /// + /// + /// Func<int, int, int, int> add3 = (x, y, z) => x + y + z; + /// var curriedAdd = add3.Curry(); + /// var result = curriedAdd(1)(2)(3); // 6 + /// + /// + public static Func>> Curry( + this Func func) + { + ArgumentNullException.ThrowIfNull(func); + return arg1 => arg2 => arg3 => func(arg1, arg2, arg3); + } + + /// + /// Uncurry:将柯里化的函数还原为多参数函数 + /// + /// 第一个参数类型 + /// 第二个参数类型 + /// 返回值类型 + /// 柯里化的函数 + /// 还原后的多参数函数 + /// 当 func 为 null 时抛出 + /// + /// + /// Func<int, Func<int, int>> curriedAdd = x => y => x + y; + /// var add = curriedAdd.Uncurry(); + /// var result = add(5, 3); // 8 + /// + /// + public static Func Uncurry( + this Func> func) + { + ArgumentNullException.ThrowIfNull(func); + return (arg1, arg2) => func(arg1)(arg2); + } + + #endregion + + #region Defer & Once + + /// + /// Defer:延迟执行函数,返回 Lazy<T> + /// + /// 返回值类型 + /// 要延迟执行的函数 + /// 包装了延迟执行的 Lazy 对象 + /// 当 func 为 null 时抛出 + /// + /// + /// var lazy = (() => ExpensiveComputation()).Defer(); + /// // 此时尚未执行 + /// var result = lazy.Value; // 首次访问时才执行 + /// + /// + public static Lazy Defer(this Func func) + { + ArgumentNullException.ThrowIfNull(func); + return new Lazy(func); + } + + /// + /// Once:确保函数只执行一次,后续调用返回缓存的结果(线程安全) + /// + /// 返回值类型 + /// 要执行的函数 + /// 包装后的函数,确保只执行一次 + /// 当 func 为 null 时抛出 + /// + /// + /// var counter = 0; + /// var once = (() => ++counter).Once(); + /// var result1 = once(); // 1 + /// var result2 = once(); // 1 (不会再次执行) + /// + /// + public static Func Once(this Func func) + { + ArgumentNullException.ThrowIfNull(func); + var lazy = new Lazy(func, LazyThreadSafetyMode.ExecutionAndPublication); + return () => lazy.Value; + } + + #endregion } \ No newline at end of file diff --git a/GFramework.Core/functional/pipe/PipeExtensions.cs b/GFramework.Core/functional/pipe/PipeExtensions.cs index 420ab54..d2e46c2 100644 --- a/GFramework.Core/functional/pipe/PipeExtensions.cs +++ b/GFramework.Core/functional/pipe/PipeExtensions.cs @@ -30,4 +30,116 @@ public static class PipeExtensions action(value); return value; } + + /// + /// Tap: + /// Also 的别名,对值执行副作用操作并返回原值 + /// 提供更符合某些编程风格的命名 + /// + /// 值的类型 + /// 要处理的值 + /// 要执行的副作用操作 + /// 原始值 + /// 当 action 为 null 时抛出 + /// + /// + /// var result = GetUser() + /// .Tap(user => Console.WriteLine($"User: {user.Name}")) + /// .Tap(user => _logger.LogInfo($"Processing user {user.Id}")); + /// + /// + public static T Tap(this T value, Action action) + { + ArgumentNullException.ThrowIfNull(action); + action(value); + return value; + } + + /// + /// Pipe: + /// 管道操作符,将值传递给函数并返回结果 + /// 用于构建流式的函数调用链 + /// + /// 输入类型 + /// 输出类型 + /// 输入值 + /// 转换函数 + /// 转换后的值 + /// 当 func 为 null 时抛出 + /// + /// + /// var result = 42 + /// .Pipe(x => x * 2) + /// .Pipe(x => x.ToString()) + /// .Pipe(s => $"Result: {s}"); + /// + /// + public static TResult Pipe( + this TSource value, + Func func) + { + ArgumentNullException.ThrowIfNull(func); + return func(value); + } + + /// + /// Let: + /// 作用域函数,将值传递给函数并返回结果 + /// 与 Pipe 功能相同,但提供不同的语义(Kotlin 风格) + /// + /// 输入类型 + /// 输出类型 + /// 输入值 + /// 转换函数 + /// 转换后的值 + /// 当 transform 为 null 时抛出 + /// + /// + /// var result = GetUser().Let(user => new UserDto + /// { + /// Id = user.Id, + /// Name = user.Name + /// }); + /// + /// + public static TResult Let( + this TSource value, + Func transform) + { + ArgumentNullException.ThrowIfNull(transform); + return transform(value); + } + + /// + /// PipeIf: + /// 条件管道,根据条件选择不同的转换函数 + /// + /// 输入类型 + /// 输出类型 + /// 输入值 + /// 条件判断函数 + /// 条件为真时的转换函数 + /// 条件为假时的转换函数 + /// 转换后的值 + /// 当任何参数为 null 时抛出 + /// + /// + /// var result = 42.PipeIf( + /// x => x > 0, + /// x => $"Positive: {x}", + /// x => $"Non-positive: {x}" + /// ); + /// + /// + public static TResult PipeIf( + this TSource value, + Func predicate, + Func ifTrue, + Func ifFalse) + { + ArgumentNullException.ThrowIfNull(predicate); + ArgumentNullException.ThrowIfNull(ifTrue); + ArgumentNullException.ThrowIfNull(ifFalse); + return predicate(value) ? ifTrue(value) : ifFalse(value); + } } \ No newline at end of file